Merge changes from topic "Remove telephony protection level" into rvc-dev
* changes:
Remove telephony protection level.
Revert "Allowed telephony to bind network service"
Revert "add telephony role to some permissions needed by telephony module"
diff --git a/Android.bp b/Android.bp
index 9c1a085..48d9559 100644
--- a/Android.bp
+++ b/Android.bp
@@ -546,7 +546,6 @@
// DO NOT ADD ANY MORE ENTRIES TO THIS LIST
"//external/robolectric-shadows:__subpackages__",
"//frameworks/layoutlib:__subpackages__",
- "//frameworks/opt/net/ike:__subpackages__",
],
}
@@ -694,6 +693,7 @@
"core/java/android/annotation/CallbackExecutor.java",
"core/java/android/annotation/CheckResult.java",
"core/java/android/annotation/CurrentTimeMillisLong.java",
+ "core/java/android/annotation/Hide.java",
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/IntRange.java",
"core/java/android/annotation/LongDef.java",
@@ -753,6 +753,18 @@
],
}
+filegroup {
+ name: "framework-services-net-module-wifi-shared-srcs",
+ srcs: [
+ "core/java/android/net/DhcpResults.java",
+ "core/java/android/net/shared/Inet4AddressUtils.java",
+ "core/java/android/net/shared/InetAddressUtils.java",
+ "core/java/android/net/util/IpUtils.java",
+ "core/java/android/util/LocalLog.java",
+ "core/java/com/android/internal/util/Preconditions.java",
+ ],
+}
+
// keep these files in sync with the package/Tethering/jarjar-rules.txt for the tethering module.
filegroup {
name: "framework-tethering-shared-srcs",
@@ -779,10 +791,6 @@
"libphonenumber-platform",
"tagsoup",
"rappor",
- "libtextclassifier-java",
- ],
- required: [
- "libtextclassifier",
],
dxflags: ["--core-library"],
}
@@ -974,7 +982,6 @@
srcs: [
"core/java/android/os/incremental/IIncrementalService.aidl",
"core/java/android/os/incremental/IncrementalNewFileParams.aidl",
- "core/java/android/os/incremental/IncrementalSignature.aidl",
],
path: "core/java",
}
@@ -1241,7 +1248,6 @@
"core/java/android/net/InterfaceConfiguration.java",
"core/java/android/os/BasicShellCommandHandler.java",
"core/java/android/util/BackupUtils.java",
- "core/java/android/util/LocalLog.java",
"core/java/android/util/Rational.java",
"core/java/com/android/internal/util/FastXmlSerializer.java",
"core/java/com/android/internal/util/HexDump.java",
@@ -1293,6 +1299,13 @@
"framework-annotations-lib",
"android.hardware.radio-V1.5-java",
],
+ check_api: {
+ current: {
+ // TODO(b/147699819): remove telephony prefix when moved
+ api_file: "telephony/api/system-current.txt",
+ removed_api_file: "telephony/api/system-removed.txt",
+ },
+ },
defaults: ["framework-module-stubs-defaults-systemapi"],
filter_packages: ["android.telephony"],
sdk_version: "system_current",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 7f23df7..da9f165 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -41,7 +41,7 @@
]
stubs_defaults {
- name: "metalava-non-updatable-api-stubs-default",
+ name: "metalava-base-api-stubs-default",
srcs: [
":framework-non-updatable-sources",
"core/java/**/*.logtags",
@@ -70,12 +70,18 @@
}
stubs_defaults {
- name: "metalava-api-stubs-default",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ name: "metalava-full-api-stubs-default",
+ defaults: ["metalava-base-api-stubs-default"],
srcs: [":framework-updatable-sources"],
sdk_version: "core_platform",
}
+stubs_defaults {
+ name: "metalava-non-updatable-api-stubs-default",
+ defaults: ["metalava-base-api-stubs-default"],
+ sdk_version: "system_current",
+}
+
/////////////////////////////////////////////////////////////////////
// *-api-stubs-docs modules providing source files for the stub libraries
/////////////////////////////////////////////////////////////////////
@@ -85,7 +91,7 @@
// modules
droidstubs {
name: "api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
api_filename: "public_api.txt",
private_api_filename: "private.txt",
removed_api_filename: "removed.txt",
@@ -124,7 +130,7 @@
droidstubs {
name: "system-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
api_tag_name: "SYSTEM",
api_filename: "system-api.txt",
private_api_filename: "system-private.txt",
@@ -155,7 +161,7 @@
droidstubs {
name: "test-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
api_tag_name: "TEST",
api_filename: "test-api.txt",
removed_api_filename: "test-removed.txt",
@@ -188,7 +194,7 @@
droidstubs {
name: "module-lib-api",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
arg_files: ["core/res/AndroidManifest.xml"],
args: metalava_framework_docs_args + module_libs,
check_api: {
@@ -216,7 +222,7 @@
droidstubs {
name: "module-lib-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-non-updatable-api-stubs-default"],
arg_files: ["core/res/AndroidManifest.xml"],
args: metalava_framework_docs_args + priv_apps + module_libs,
}
@@ -266,6 +272,7 @@
name: "android_module_lib_stubs_current",
srcs: [ ":module-lib-api-stubs-docs" ],
defaults: ["framework-stubs-default"],
+ libs: ["android_system_stubs_current"],
}
/////////////////////////////////////////////////////////////////////
@@ -317,7 +324,7 @@
droidstubs {
name: "hiddenapi-lists-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
arg_files: [
"core/res/AndroidManifest.xml",
],
@@ -332,7 +339,7 @@
droidstubs {
name: "hiddenapi-mappings",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
srcs: [
":opt-telephony-common-srcs",
],
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 02df5e2..23f025b 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -121,8 +121,9 @@
}
private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
- final DummyBlobData blobData = new DummyBlobData(mContext,
- fileSizeInMb * 1024 * 1024 /* bytes */);
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+ .setFileSize(fileSizeInMb * 1024 * 1024 /* bytes */)
+ .build();
blobData.prepare();
return blobData;
}
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index c458d11..e042782 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -670,7 +670,7 @@
private void startApp(int userId, String packageName) throws RemoteException {
final Context context = InstrumentationRegistry.getContext();
final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null,
- context.getPackageName(), context.getFeatureId(),
+ context.getPackageName(), context.getAttributionTag(),
context.getPackageManager().getLaunchIntentForPackage(packageName), null, null,
null, 0, 0, null, null, userId);
attestTrue("User " + userId + " failed to start " + packageName,
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
index f61ea85..46250d7 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
@@ -77,7 +77,6 @@
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
textClassificationManager.getTextClassifier();
- textClassificationManager.invalidateForTesting();
}
}
@@ -90,7 +89,6 @@
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
textClassificationManager.getTextClassifier();
- textClassificationManager.invalidateForTesting();
}
}
diff --git a/apex/Android.bp b/apex/Android.bp
index 1510911..1943940 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -44,14 +44,26 @@
args: mainline_stubs_args,
installable: false,
sdk_version: "current",
+ check_api: {
+ current: {
+ api_file: "api/current.txt",
+ removed_api_file: "api/removed.txt",
+ },
+ },
}
stubs_defaults {
name: "framework-module-stubs-defaults-systemapi",
args: mainline_stubs_args + priv_apps,
- srcs: [":framework-annotations"],
+ libs: ["framework-annotations-lib"],
installable: false,
sdk_version: "system_current",
+ check_api: {
+ current: {
+ api_file: "api/system-current.txt",
+ removed_api_file: "api/system-removed.txt",
+ },
+ },
}
// The defaults for module_libs comes in two parts - defaults for API checks
@@ -62,15 +74,21 @@
stubs_defaults {
name: "framework-module-api-defaults-module_libs_api",
args: mainline_stubs_args + module_libs,
- srcs: [":framework-annotations"],
+ libs: ["framework-annotations-lib"],
installable: false,
sdk_version: "module_current",
+ check_api: {
+ current: {
+ api_file: "api/module-lib-current.txt",
+ removed_api_file: "api/module-lib-removed.txt",
+ },
+ },
}
stubs_defaults {
name: "framework-module-stubs-defaults-module_libs_api",
args: mainline_stubs_args + module_libs + priv_apps,
- srcs: [":framework-annotations"],
+ libs: ["framework-annotations-lib"],
installable: false,
sdk_version: "module_current",
}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
index 9746dd0..80062d5 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
@@ -32,21 +32,21 @@
private final long mId;
private final long mExpiryTimeMs;
private final CharSequence mLabel;
- private final List<AccessorInfo> mAccessors;
+ private final List<LeaseInfo> mLeaseInfos;
public BlobInfo(long id, long expiryTimeMs, CharSequence label,
- List<AccessorInfo> accessors) {
+ List<LeaseInfo> leaseInfos) {
mId = id;
mExpiryTimeMs = expiryTimeMs;
mLabel = label;
- mAccessors = accessors;
+ mLeaseInfos = leaseInfos;
}
private BlobInfo(Parcel in) {
mId = in.readLong();
mExpiryTimeMs = in.readLong();
mLabel = in.readCharSequence();
- mAccessors = in.readArrayList(null /* classloader */);
+ mLeaseInfos = in.readArrayList(null /* classloader */);
}
public long getId() {
@@ -61,8 +61,8 @@
return mLabel;
}
- public List<AccessorInfo> getAccessors() {
- return Collections.unmodifiableList(mAccessors);
+ public List<LeaseInfo> getLeases() {
+ return Collections.unmodifiableList(mLeaseInfos);
}
@Override
@@ -70,7 +70,7 @@
dest.writeLong(mId);
dest.writeLong(mExpiryTimeMs);
dest.writeCharSequence(mLabel);
- dest.writeList(mAccessors);
+ dest.writeList(mLeaseInfos);
}
@Override
@@ -83,7 +83,7 @@
+ "id: " + mId + ","
+ "expiryMs: " + mExpiryTimeMs + ","
+ "label: " + mLabel + ","
- + "accessors: " + AccessorInfo.toShortString(mAccessors) + ","
+ + "leases: " + LeaseInfo.toShortString(mLeaseInfos) + ","
+ "}";
}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 814ab6d..c339351 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -21,6 +21,7 @@
import android.annotation.IdRes;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
@@ -522,6 +523,50 @@
}
/**
+ * Return the {@link BlobHandle BlobHandles} corresponding to the data blobs that
+ * the calling app has acquired a lease on using {@link #acquireLease(BlobHandle, int)} or
+ * one of it's other variants.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public List<BlobHandle> getLeasedBlobs() throws IOException {
+ try {
+ return mService.getLeasedBlobs(mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return {@link LeaseInfo} representing a lease acquired using
+ * {@link #acquireLease(BlobHandle, int)} or one of it's other variants,
+ * or {@code null} if there is no lease acquired.
+ *
+ * @throws SecurityException when the blob represented by the {@code blobHandle} does not
+ * exist or the caller does not have access to it.
+ * @throws IllegalArgumentException when {@code blobHandle} is invalid.
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle) throws IOException {
+ try {
+ return mService.getLeaseInfo(blobHandle, mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Represents an ongoing session of a blob's contribution to the blob store managed by the
* system.
*
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index e783813..20c15ab 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -18,6 +18,7 @@
import android.app.blob.BlobHandle;
import android.app.blob.BlobInfo;
import android.app.blob.IBlobStoreSession;
+import android.app.blob.LeaseInfo;
import android.os.RemoteCallback;
/** {@hide} */
@@ -35,4 +36,7 @@
List<BlobInfo> queryBlobsForUser(int userId);
void deleteBlob(long blobId);
+
+ List<BlobHandle> getLeasedBlobs(in String packageName);
+ LeaseInfo getLeaseInfo(in BlobHandle blobHandle, in String packageName);
}
\ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/LeaseInfo.aidl b/apex/blobstore/framework/java/android/app/blob/LeaseInfo.aidl
new file mode 100644
index 0000000..9088857
--- /dev/null
+++ b/apex/blobstore/framework/java/android/app/blob/LeaseInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.app.blob;
+
+/** {@hide} */
+parcelable LeaseInfo;
\ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java b/apex/blobstore/framework/java/android/app/blob/LeaseInfo.java
similarity index 61%
rename from apex/blobstore/framework/java/android/app/blob/AccessorInfo.java
rename to apex/blobstore/framework/java/android/app/blob/LeaseInfo.java
index 3725ad4..fef50c9 100644
--- a/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java
+++ b/apex/blobstore/framework/java/android/app/blob/LeaseInfo.java
@@ -16,50 +16,61 @@
package android.app.blob;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IdRes;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.List;
/**
- * Class to provide information about an accessor of a shared blob.
+ * Class to provide information about a lease (acquired using
+ * {@link BlobStoreManager#acquireLease(BlobHandle, int)} or one of it's variants)
+ * for a shared blob.
*
* @hide
*/
-public final class AccessorInfo implements Parcelable {
+@TestApi
+public final class LeaseInfo implements Parcelable {
private final String mPackageName;
- private final long mExpiryTimeMs;
+ private final long mExpiryTimeMillis;
private final int mDescriptionResId;
private final CharSequence mDescription;
- public AccessorInfo(String packageName, long expiryTimeMs,
- int descriptionResId, CharSequence description) {
+ public LeaseInfo(@NonNull String packageName, @CurrentTimeMillisLong long expiryTimeMs,
+ @IdRes int descriptionResId, @Nullable CharSequence description) {
mPackageName = packageName;
- mExpiryTimeMs = expiryTimeMs;
+ mExpiryTimeMillis = expiryTimeMs;
mDescriptionResId = descriptionResId;
mDescription = description;
}
- private AccessorInfo(Parcel in) {
+ private LeaseInfo(Parcel in) {
mPackageName = in.readString();
- mExpiryTimeMs = in.readLong();
+ mExpiryTimeMillis = in.readLong();
mDescriptionResId = in.readInt();
mDescription = in.readCharSequence();
}
+ @NonNull
public String getPackageName() {
return mPackageName;
}
- public long getExpiryTimeMs() {
- return mExpiryTimeMs;
+ @CurrentTimeMillisLong
+ public long getExpiryTimeMillis() {
+ return mExpiryTimeMillis;
}
+ @IdRes
public int getDescriptionResId() {
return mDescriptionResId;
}
+ @Nullable
public CharSequence getDescription() {
return mDescription;
}
@@ -67,16 +78,16 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mPackageName);
- dest.writeLong(mExpiryTimeMs);
+ dest.writeLong(mExpiryTimeMillis);
dest.writeInt(mDescriptionResId);
dest.writeCharSequence(mDescription);
}
@Override
public String toString() {
- return "AccessorInfo {"
+ return "LeaseInfo {"
+ "package: " + mPackageName + ","
- + "expiryMs: " + mExpiryTimeMs + ","
+ + "expiryMs: " + mExpiryTimeMillis + ","
+ "descriptionResId: " + mDescriptionResId + ","
+ "description: " + mDescription + ","
+ "}";
@@ -86,11 +97,11 @@
return mPackageName;
}
- public static String toShortString(List<AccessorInfo> accessors) {
+ static String toShortString(List<LeaseInfo> leaseInfos) {
final StringBuilder sb = new StringBuilder();
sb.append("[");
- for (int i = 0, size = accessors.size(); i < size; ++i) {
- sb.append(accessors.get(i).toShortString());
+ for (int i = 0, size = leaseInfos.size(); i < size; ++i) {
+ sb.append(leaseInfos.get(i).toShortString());
sb.append(",");
}
sb.append("]");
@@ -103,17 +114,17 @@
}
@NonNull
- public static final Creator<AccessorInfo> CREATOR = new Creator<AccessorInfo>() {
+ public static final Creator<LeaseInfo> CREATOR = new Creator<LeaseInfo>() {
@Override
@NonNull
- public AccessorInfo createFromParcel(Parcel source) {
- return new AccessorInfo(source);
+ public LeaseInfo createFromParcel(Parcel source) {
+ return new LeaseInfo(source);
}
@Override
@NonNull
- public AccessorInfo[] newArray(int size) {
- return new AccessorInfo[size];
+ public LeaseInfo[] newArray(int size) {
+ return new LeaseInfo[size];
}
};
}
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 970766d..8b640ca 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -38,6 +38,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.blob.BlobHandle;
+import android.app.blob.LeaseInfo;
import android.content.Context;
import android.content.res.ResourceId;
import android.content.res.Resources;
@@ -281,6 +282,25 @@
return false;
}
+ @Nullable
+ LeaseInfo getLeaseInfo(@NonNull String packageName, int uid) {
+ synchronized (mMetadataLock) {
+ for (int i = 0, size = mLeasees.size(); i < size; ++i) {
+ final Leasee leasee = mLeasees.valueAt(i);
+ if (leasee.uid == uid && leasee.packageName.equals(packageName)) {
+ final int descriptionResId = leasee.descriptionResEntryName == null
+ ? Resources.ID_NULL
+ : BlobStoreUtils.getDescriptionResourceId(
+ mContext, leasee.descriptionResEntryName, leasee.packageName,
+ UserHandle.getUserId(leasee.uid));
+ return new LeaseInfo(packageName, leasee.expiryTimeMillis,
+ descriptionResId, leasee.description);
+ }
+ }
+ }
+ return null;
+ }
+
void forEachLeasee(Consumer<Leasee> consumer) {
mLeasees.forEach(consumer);
}
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 53a97ce..f4b8f0f 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -45,11 +45,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.blob.AccessorInfo;
import android.app.blob.BlobHandle;
import android.app.blob.BlobInfo;
import android.app.blob.IBlobStoreManager;
import android.app.blob.IBlobStoreSession;
+import android.app.blob.LeaseInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -454,17 +454,17 @@
return packageResources;
};
getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
- final ArrayList<AccessorInfo> accessorInfos = new ArrayList<>();
+ final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>();
blobMetadata.forEachLeasee(leasee -> {
final int descriptionResId = leasee.descriptionResEntryName == null
? Resources.ID_NULL
: getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
leasee.descriptionResEntryName, leasee.packageName);
- accessorInfos.add(new AccessorInfo(leasee.packageName, leasee.expiryTimeMillis,
+ leaseInfos.add(new LeaseInfo(leasee.packageName, leasee.expiryTimeMillis,
descriptionResId, leasee.description));
});
blobInfos.add(new BlobInfo(blobMetadata.getBlobId(),
- blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(), accessorInfos));
+ blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(), leaseInfos));
});
}
return blobInfos;
@@ -482,6 +482,31 @@
}
}
+ private List<BlobHandle> getLeasedBlobsInternal(int callingUid,
+ @NonNull String callingPackage) {
+ final ArrayList<BlobHandle> leasedBlobs = new ArrayList<>();
+ forEachBlobInUser(blobMetadata -> {
+ if (blobMetadata.isALeasee(callingPackage, callingUid)) {
+ leasedBlobs.add(blobMetadata.getBlobHandle());
+ }
+ }, UserHandle.getUserId(callingUid));
+ return leasedBlobs;
+ }
+
+ private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle,
+ int callingUid, @NonNull String callingPackage) {
+ synchronized (mBlobsLock) {
+ final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
+ .get(blobHandle);
+ if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
+ callingPackage, callingUid)) {
+ throw new SecurityException("Caller not allowed to access " + blobHandle
+ + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
+ }
+ return blobMetadata.getLeaseInfo(callingPackage, callingUid);
+ }
+ }
+
private void verifyCallingPackage(int callingUid, String callingPackage) {
if (mPackageManagerInternal.getPackageUid(
callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
@@ -1267,6 +1292,12 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
+ if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
+ packageName, UserHandle.getUserId(callingUid))) {
+ throw new SecurityException("Caller not allowed to open blob; "
+ + "callingUid=" + callingUid + ", callingPackage=" + packageName);
+ }
+
try {
acquireLeaseInternal(blobHandle, descriptionResId, description,
leaseExpiryTimeMillis, callingUid, packageName);
@@ -1284,6 +1315,12 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
+ if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
+ packageName, UserHandle.getUserId(callingUid))) {
+ throw new SecurityException("Caller not allowed to open blob; "
+ + "callingUid=" + callingUid + ", callingPackage=" + packageName);
+ }
+
releaseLeaseInternal(blobHandle, callingUid, packageName);
}
@@ -1320,6 +1357,36 @@
}
@Override
+ @NonNull
+ public List<BlobHandle> getLeasedBlobs(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ final int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+
+ return getLeasedBlobsInternal(callingUid, packageName);
+ }
+
+ @Override
+ @Nullable
+ public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
+ Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+ blobHandle.assertIsValid();
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ final int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+
+ if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
+ packageName, UserHandle.getUserId(callingUid))) {
+ throw new SecurityException("Caller not allowed to open blob; "
+ + "callingUid=" + callingUid + ", callingPackage=" + packageName);
+ }
+
+ return getLeaseInfoInternal(blobHandle, callingUid, packageName);
+ }
+
+ @Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
// TODO: add proto-based version of this.
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
index 6af540a..fabce76 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -47,4 +47,13 @@
@NonNull String resourceEntryName, @NonNull String packageName) {
return resources.getIdentifier(resourceEntryName, DESC_RES_TYPE_STRING, packageName);
}
+
+ @IdRes
+ static int getDescriptionResourceId(@NonNull Context context,
+ @NonNull String resourceEntryName, @NonNull String packageName, int userId) {
+ final Resources resources = getPackageResources(context, packageName, userId);
+ return resources == null
+ ? Resources.ID_NULL
+ : getDescriptionResourceId(resources, resourceEntryName, packageName);
+ }
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index b905273..ae8976a 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -53,9 +53,8 @@
* Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
* parameters required to schedule work against the calling application. These are constructed
* using the {@link JobInfo.Builder}.
- * You must specify at least one sort of constraint on the JobInfo object that you are creating.
* The goal here is to provide the scheduler with high-level semantics about the work you want to
- * accomplish. Doing otherwise with throw an exception in your app.
+ * accomplish.
*/
public class JobInfo implements Parcelable {
private static String TAG = "JobInfo";
@@ -147,7 +146,7 @@
/**
* Query the minimum interval allowed for periodic scheduled jobs. Attempting
- * to declare a smaller period that this when scheduling a job will result in a
+ * to declare a smaller period than this when scheduling a job will result in a
* job that is still periodic, but will run with this effective period.
*
* @return The minimum available interval for scheduling periodic jobs, in milliseconds.
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index f8b598a..6d9e3ed 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -5,6 +5,7 @@
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManager.SystemForcedReasons;
import android.content.Context;
import android.os.Looper;
@@ -123,9 +124,10 @@
* appropriate time.
*
* @param restrictReason The restrictReason for restricting the app. Should be one of the
- * UsageStatsManager.REASON_SUB_RESTRICT_* reasons.
+ * UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_* reasons.
*/
- void restrictApp(@NonNull String packageName, int userId, int restrictReason);
+ void restrictApp(@NonNull String packageName, int userId,
+ @SystemForcedReasons int restrictReason);
void addActiveDeviceAdmin(String adminPkg, int userId);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index fc29c9c..819f253 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -974,7 +974,7 @@
if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
mAppStandbyInternal.restrictApp(
- pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY);
+ pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
final boolean isDebuggable;
synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 249bc52..b6f85b2 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -23,6 +23,8 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
@@ -58,6 +60,7 @@
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManager.SystemForcedReasons;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -72,7 +75,6 @@
import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
-import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -303,10 +305,7 @@
private final AppStandbyHandler mHandler;
private final Context mContext;
- // TODO: Provide a mechanism to set an external bucketing service
-
private AppWidgetManager mAppWidgetManager;
- private ConnectivityManager mConnectivityManager;
private PackageManager mPackageManager;
Injector mInjector;
@@ -410,7 +409,6 @@
settingsObserver.updateSettings();
mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
- mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mInjector.registerDisplayListener(mDisplayListener, mHandler);
synchronized (mAppIdleLock) {
@@ -1153,6 +1151,13 @@
}
}
+ @VisibleForTesting
+ int getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime);
+ }
+ }
+
@Override
public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
synchronized (mAppIdleLock) {
@@ -1161,7 +1166,8 @@
}
@Override
- public void restrictApp(@NonNull String packageName, int userId, int restrictReason) {
+ public void restrictApp(@NonNull String packageName, int userId,
+ @SystemForcedReasons int restrictReason) {
// If the package is not installed, don't allow the bucket to be set.
if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
@@ -1248,30 +1254,52 @@
// Don't allow changing bucket if higher than ACTIVE
if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
- // Don't allow prediction to change from/to NEVER or from RESTRICTED.
- if ((app.currentBucket == STANDBY_BUCKET_NEVER
- || app.currentBucket == STANDBY_BUCKET_RESTRICTED
- || newBucket == STANDBY_BUCKET_NEVER)
+ // Don't allow prediction to change from/to NEVER.
+ if ((app.currentBucket == STANDBY_BUCKET_NEVER || newBucket == STANDBY_BUCKET_NEVER)
&& predicted) {
return;
}
+ final boolean wasForcedBySystem =
+ (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
+
// If the bucket was forced, don't allow prediction to override
if (predicted
&& ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER
- || (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM)) {
+ || wasForcedBySystem)) {
+ return;
+ }
+
+ final boolean isForcedBySystem =
+ (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
+
+ if (app.currentBucket == newBucket && wasForcedBySystem && isForcedBySystem) {
+ mAppIdleHistory
+ .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
+ // Keep track of all restricting reasons
+ reason = REASON_MAIN_FORCED_BY_SYSTEM
+ | (app.bucketingReason & REASON_SUB_MASK)
+ | (reason & REASON_SUB_MASK);
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+ newBucket, reason, resetTimeout);
return;
}
final boolean isForcedByUser =
(reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
- // If the current bucket is RESTRICTED, only user force or usage should bring it out,
- // unless the app was put into the bucket due to timing out.
- if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason)
- && !isForcedByUser
- && (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) {
- return;
+ if (app.currentBucket == STANDBY_BUCKET_RESTRICTED) {
+ if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_TIMEOUT) {
+ if (predicted && newBucket >= STANDBY_BUCKET_RARE) {
+ // Predicting into RARE or below means we don't expect the user to use the
+ // app anytime soon, so don't elevate it from RESTRICTED.
+ return;
+ }
+ } else if (!isUserUsage(reason) && !isForcedByUser) {
+ // If the current bucket is RESTRICTED, only user force or usage should bring
+ // it out, unless the app was put into the bucket due to timing out.
+ return;
+ }
}
if (newBucket == STANDBY_BUCKET_RESTRICTED) {
@@ -1488,6 +1516,38 @@
}
}
+ /**
+ * Remove an app from the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
+ * bucket if it was forced into the bucket by the system because it was buggy.
+ */
+ @VisibleForTesting
+ void maybeUnrestrictBuggyApp(String packageName, int userId) {
+ synchronized (mAppIdleLock) {
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ final AppIdleHistory.AppUsageHistory app =
+ mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime);
+ if (app.currentBucket != STANDBY_BUCKET_RESTRICTED
+ || (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_FORCED_BY_SYSTEM) {
+ return;
+ }
+
+ final int newBucket;
+ final int newReason;
+ if ((app.bucketingReason & REASON_SUB_MASK) == REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY) {
+ // If bugginess was the only reason the app should be restricted, then lift it out.
+ newBucket = STANDBY_BUCKET_RARE;
+ newReason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_UPDATE;
+ } else {
+ // There's another reason the app was restricted. Remove the buggy bit and call
+ // it a day.
+ newBucket = STANDBY_BUCKET_RESTRICTED;
+ newReason = app.bucketingReason & ~REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
+ }
+ mAppIdleHistory.setAppStandbyBucket(
+ packageName, userId, elapsedRealtime, newBucket, newReason);
+ }
+ }
+
private class PackageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1497,10 +1557,14 @@
clearCarrierPrivilegedApps();
}
if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_ADDED.equals(action))
- && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
- getSendingUserId());
+ Intent.ACTION_PACKAGE_ADDED.equals(action))) {
+ final String pkgName = intent.getData().getSchemeSpecificPart();
+ final int userId = getSendingUserId();
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ maybeUnrestrictBuggyApp(pkgName, userId);
+ } else {
+ clearAppIdleForPackage(pkgName, userId);
+ }
}
}
}
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index 0ac750c..9b853c5 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,4 +1,4 @@
andrewlewis@google.com
-dwkang@google.com
+aquilescanta@google.com
marcone@google.com
sungsoo@google.com
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
new file mode 100644
index 0000000..2b7dcd33
--- /dev/null
+++ b/apex/media/framework/api/current.txt
@@ -0,0 +1,206 @@
+// Signature format: 2.0
+package android.media {
+
+ public class MediaController2 implements java.lang.AutoCloseable {
+ method public void cancelSessionCommand(@NonNull Object);
+ method public void close();
+ method @Nullable public android.media.Session2Token getConnectedToken();
+ method public boolean isPlaybackActive();
+ method @NonNull public Object sendSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle);
+ }
+
+ public static final class MediaController2.Builder {
+ ctor public MediaController2.Builder(@NonNull android.content.Context, @NonNull android.media.Session2Token);
+ method @NonNull public android.media.MediaController2 build();
+ method @NonNull public android.media.MediaController2.Builder setConnectionHints(@NonNull android.os.Bundle);
+ method @NonNull public android.media.MediaController2.Builder setControllerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaController2.ControllerCallback);
+ }
+
+ public abstract static class MediaController2.ControllerCallback {
+ ctor public MediaController2.ControllerCallback();
+ method public void onCommandResult(@NonNull android.media.MediaController2, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result);
+ method public void onConnected(@NonNull android.media.MediaController2, @NonNull android.media.Session2CommandGroup);
+ method public void onDisconnected(@NonNull android.media.MediaController2);
+ method public void onPlaybackActiveChanged(@NonNull android.media.MediaController2, boolean);
+ method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaController2, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
+ }
+
+ public final class MediaParser {
+ method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException;
+ method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...);
+ method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer);
+ method @Nullable public String getParserName();
+ method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat);
+ method public void release();
+ method public void seek(@NonNull android.media.MediaParser.SeekPoint);
+ method @NonNull public android.media.MediaParser setParameter(@NonNull String, @NonNull Object);
+ method public boolean supportsParameter(@NonNull String);
+ field public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = "android.media.mediaparser.adts.enableCbrSeeking";
+ field public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "android.media.mediaparser.amr.enableCbrSeeking";
+ field public static final String PARAMETER_FLAC_DISABLE_ID3 = "android.media.mediaparser.flac.disableId3";
+ field public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = "android.media.mediaparser.matroska.disableCuesSeeking";
+ field public static final String PARAMETER_MP3_DISABLE_ID3 = "android.media.mediaparser.mp3.disableId3";
+ field public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "android.media.mediaparser.mp3.enableCbrSeeking";
+ field public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = "android.media.mediaparser.mp3.enableIndexSeeking";
+ field public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "android.media.mediaparser.mp4.ignoreEditLists";
+ field public static final String PARAMETER_MP4_IGNORE_TFDT_BOX = "android.media.mediaparser.mp4.ignoreTfdtBox";
+ field public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes";
+ field public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = "android.media.mediaparser.ts.allowNonIdrAvcKeyframes";
+ field public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = "android.media.mediaparser.ts.ignoreDetectAccessUnits";
+ field public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = "android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
+ field public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "android.media.mediaparser.ts.ignoreAacStream";
+ field public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "android.media.mediaparser.ts.ignoreAvcStream";
+ field public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = "android.media.mediaparser.ts.ignoreSpliceInfoStream";
+ field public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode";
+ }
+
+ public static interface MediaParser.InputReader {
+ method public long getLength();
+ method public long getPosition();
+ method public int read(@NonNull byte[], int, int) throws java.io.IOException;
+ }
+
+ public static interface MediaParser.OutputConsumer {
+ method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
+ method public void onSampleDataFound(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException;
+ method public void onSeekMapFound(@NonNull android.media.MediaParser.SeekMap);
+ method public void onTrackCountFound(int);
+ method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData);
+ }
+
+ public static final class MediaParser.ParsingException extends java.io.IOException {
+ }
+
+ public static final class MediaParser.SeekMap {
+ method public long getDurationMicros();
+ method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
+ method public boolean isSeekable();
+ field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000
+ }
+
+ public static final class MediaParser.SeekPoint {
+ field @NonNull public static final android.media.MediaParser.SeekPoint START;
+ field public final long position;
+ field public final long timeMicros;
+ }
+
+ public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader {
+ method public void seekToPosition(long);
+ }
+
+ public static final class MediaParser.TrackData {
+ field @Nullable public final android.media.DrmInitData drmInitData;
+ field @NonNull public final android.media.MediaFormat mediaFormat;
+ }
+
+ public static final class MediaParser.UnrecognizedInputFormatException extends java.io.IOException {
+ }
+
+ public class MediaSession2 implements java.lang.AutoCloseable {
+ method public void broadcastSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle);
+ method public void cancelSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object);
+ method public void close();
+ method @NonNull public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
+ method @NonNull public String getId();
+ method @NonNull public android.media.Session2Token getToken();
+ method public boolean isPlaybackActive();
+ method @NonNull public Object sendSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
+ method public void setPlaybackActive(boolean);
+ }
+
+ public static final class MediaSession2.Builder {
+ ctor public MediaSession2.Builder(@NonNull android.content.Context);
+ method @NonNull public android.media.MediaSession2 build();
+ method @NonNull public android.media.MediaSession2.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.media.MediaSession2.Builder setId(@NonNull String);
+ method @NonNull public android.media.MediaSession2.Builder setSessionActivity(@Nullable android.app.PendingIntent);
+ method @NonNull public android.media.MediaSession2.Builder setSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaSession2.SessionCallback);
+ }
+
+ public static final class MediaSession2.ControllerInfo {
+ method @NonNull public android.os.Bundle getConnectionHints();
+ method @NonNull public String getPackageName();
+ method @NonNull public android.media.session.MediaSessionManager.RemoteUserInfo getRemoteUserInfo();
+ method public int getUid();
+ }
+
+ public abstract static class MediaSession2.SessionCallback {
+ ctor public MediaSession2.SessionCallback();
+ method public void onCommandResult(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result);
+ method @Nullable public android.media.Session2CommandGroup onConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
+ method public void onDisconnected(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
+ method public void onPostConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
+ method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
+ }
+
+ public abstract class MediaSession2Service extends android.app.Service {
+ ctor public MediaSession2Service();
+ method public final void addSession(@NonNull android.media.MediaSession2);
+ method @NonNull public final java.util.List<android.media.MediaSession2> getSessions();
+ method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public abstract android.media.MediaSession2 onGetSession(@NonNull android.media.MediaSession2.ControllerInfo);
+ method @Nullable public abstract android.media.MediaSession2Service.MediaNotification onUpdateNotification(@NonNull android.media.MediaSession2);
+ method public final void removeSession(@NonNull android.media.MediaSession2);
+ field public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
+ }
+
+ public static class MediaSession2Service.MediaNotification {
+ ctor public MediaSession2Service.MediaNotification(int, @NonNull android.app.Notification);
+ method @NonNull public android.app.Notification getNotification();
+ method public int getNotificationId();
+ }
+
+ public final class Session2Command implements android.os.Parcelable {
+ ctor public Session2Command(int);
+ ctor public Session2Command(@NonNull String, @Nullable android.os.Bundle);
+ method public int describeContents();
+ method public int getCommandCode();
+ method @Nullable public String getCustomAction();
+ method @Nullable public android.os.Bundle getCustomExtras();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Command> CREATOR;
+ }
+
+ public static final class Session2Command.Result {
+ ctor public Session2Command.Result(int, @Nullable android.os.Bundle);
+ method public int getResultCode();
+ method @Nullable public android.os.Bundle getResultData();
+ field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+ field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ }
+
+ public final class Session2CommandGroup implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Set<android.media.Session2Command> getCommands();
+ method public boolean hasCommand(@NonNull android.media.Session2Command);
+ method public boolean hasCommand(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2CommandGroup> CREATOR;
+ }
+
+ public static final class Session2CommandGroup.Builder {
+ ctor public Session2CommandGroup.Builder();
+ ctor public Session2CommandGroup.Builder(@NonNull android.media.Session2CommandGroup);
+ method @NonNull public android.media.Session2CommandGroup.Builder addCommand(@NonNull android.media.Session2Command);
+ method @NonNull public android.media.Session2CommandGroup build();
+ method @NonNull public android.media.Session2CommandGroup.Builder removeCommand(@NonNull android.media.Session2Command);
+ }
+
+ public final class Session2Token implements android.os.Parcelable {
+ ctor public Session2Token(@NonNull android.content.Context, @NonNull android.content.ComponentName);
+ method public int describeContents();
+ method @NonNull public android.os.Bundle getExtras();
+ method @NonNull public String getPackageName();
+ method @Nullable public String getServiceName();
+ method public int getType();
+ method public int getUid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Token> CREATOR;
+ field public static final int TYPE_SESSION = 0; // 0x0
+ field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+ }
+
+}
+
diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/framework/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/framework/api/module-lib-removed.txt b/apex/media/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/framework/api/removed.txt b/apex/media/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/framework/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/framework/api/system-removed.txt b/apex/media/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/media/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index b219fd41..bb3f4e9 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -25,6 +25,7 @@
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
@@ -103,12 +104,12 @@
* private int bytesWrittenCount = 0;
*
* @Override
- * public void onSeekMap(int i, @NonNull MediaFormat mediaFormat) {
+ * public void onSeekMapFound(int i, @NonNull MediaFormat mediaFormat) {
* // Do nothing.
* }
*
* @Override
- * public void onTrackData(int i, @NonNull TrackData trackData) {
+ * public void onTrackDataFound(int i, @NonNull TrackData trackData) {
* MediaFormat mediaFormat = trackData.mediaFormat;
* if (videoTrackIndex == -1 &&
* mediaFormat
@@ -119,7 +120,7 @@
* }
*
* @Override
- * public void onSampleData(int trackIndex, @NonNull InputReader inputReader)
+ * public void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader)
* throws IOException {
* int numberOfBytesToRead = (int) inputReader.getLength();
* if (videoTrackIndex != trackIndex) {
@@ -139,7 +140,7 @@
* @Override
* public void onSampleCompleted(
* int trackIndex,
- * long timeUs,
+ * long timeMicros,
* int flags,
* int size,
* int offset,
@@ -163,7 +164,7 @@
* /* destPos= */ 0,
* /* size= */ offset);
* bytesWrittenCount = bytesWrittenCount - offset;
- * publishSample(sampleData, timeUs, flags);
+ * publishSample(sampleData, timeMicros, flags);
* }
*
* private void ensureSpaceInBuffer(int numberOfBytesToRead) {
@@ -187,7 +188,7 @@
*/
public static final class SeekMap {
- /** Returned by {@link #getDurationUs()} when the duration is unknown. */
+ /** Returned by {@link #getDurationMicros()} when the duration is unknown. */
public static final int UNKNOWN_DURATION = Integer.MIN_VALUE;
private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
@@ -205,26 +206,26 @@
* Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the
* duration is unknown.
*/
- public long getDurationUs() {
+ public long getDurationMicros() {
return mExoPlayerSeekMap.getDurationUs();
}
/**
* Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds.
*
- * <p>{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with
- * timestamp equal to or smaller than {@code timeUs}.
+ * <p>{@code getSeekPoints(timeMicros).first} contains the latest seek point for samples
+ * with timestamp equal to or smaller than {@code timeMicros}.
*
- * <p>{@code getSeekPoints(timeUs).second} contains the earliest seek point for samples with
- * timestamp equal to or greater than {@code timeUs}. If a seek point exists for {@code
- * timeUs}, the returned pair will contain the same {@link SeekPoint} twice.
+ * <p>{@code getSeekPoints(timeMicros).second} contains the earliest seek point for samples
+ * with timestamp equal to or greater than {@code timeMicros}. If a seek point exists for
+ * {@code timeMicros}, the returned pair will contain the same {@link SeekPoint} twice.
*
- * @param timeUs A seek time in microseconds.
+ * @param timeMicros A seek time in microseconds.
* @return The corresponding {@link SeekPoint SeekPoints}.
*/
@NonNull
- public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
- SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
+ public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeMicros) {
+ SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeMicros);
return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
}
}
@@ -254,24 +255,24 @@
@NonNull public static final SeekPoint START = new SeekPoint(0, 0);
/** The time of the seek point, in microseconds. */
- public final long timeUs;
+ public final long timeMicros;
/** The byte offset of the seek point. */
public final long position;
/**
- * @param timeUs The time of the seek point, in microseconds.
+ * @param timeMicros The time of the seek point, in microseconds.
* @param position The byte offset of the seek point.
*/
- private SeekPoint(long timeUs, long position) {
- this.timeUs = timeUs;
+ private SeekPoint(long timeMicros, long position) {
+ this.timeMicros = timeMicros;
this.position = position;
}
@Override
@NonNull
public String toString() {
- return "[timeUs=" + timeUs + ", position=" + position + "]";
+ return "[timeMicros=" + timeMicros + ", position=" + position + "]";
}
@Override
@@ -283,12 +284,12 @@
return false;
}
SeekPoint other = (SeekPoint) obj;
- return timeUs == other.timeUs && position == other.position;
+ return timeMicros == other.timeMicros && position == other.position;
}
@Override
public int hashCode() {
- int result = (int) timeUs;
+ int result = (int) timeMicros;
result = 31 * result + (int) position;
return result;
}
@@ -345,25 +346,25 @@
*
* @param seekMap The extracted {@link SeekMap}.
*/
- void onSeekMap(@NonNull SeekMap seekMap);
+ void onSeekMapFound(@NonNull SeekMap seekMap);
/**
* Called when the number of tracks is found.
*
* @param numberOfTracks The number of tracks in the stream.
*/
- void onTracksFound(int numberOfTracks);
+ void onTrackCountFound(int numberOfTracks);
/**
- * Called when new {@link TrackData} is extracted from the stream.
+ * Called when new {@link TrackData} is found in the stream.
*
* @param trackIndex The index of the track for which the {@link TrackData} was extracted.
* @param trackData The extracted {@link TrackData}.
*/
- void onTrackData(int trackIndex, @NonNull TrackData trackData);
+ void onTrackDataFound(int trackIndex, @NonNull TrackData trackData);
/**
- * Called to write sample data to the output.
+ * Called when sample data is found in the stream.
*
* <p>If the invocation of this method returns before the entire {@code inputReader} {@link
* InputReader#getLength() length} is consumed, the method will be called again for the
@@ -374,27 +375,27 @@
* @param inputReader The {@link InputReader} from which to read the data.
* @throws IOException If an exception occurs while reading from {@code inputReader}.
*/
- void onSampleData(int trackIndex, @NonNull InputReader inputReader) throws IOException;
+ void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) throws IOException;
/**
- * Called once all the data of a sample has been passed to {@link #onSampleData}.
+ * Called once all the data of a sample has been passed to {@link #onSampleDataFound}.
*
* <p>Also includes sample metadata, like presentation timestamp and flags.
*
* @param trackIndex The index of the track to which the sample corresponds.
- * @param timeUs The media timestamp associated with the sample, in microseconds.
+ * @param timeMicros The media timestamp associated with the sample, in microseconds.
* @param flags Flags associated with the sample. See {@link MediaCodec
* MediaCodec.BUFFER_FLAG_*}.
* @param size The size of the sample data, in bytes.
- * @param offset The number of bytes that have been consumed by {@code onSampleData(int,
- * MediaParser.InputReader)} for the specified track, since the last byte belonging to
- * the sample whose metadata is being passed.
+ * @param offset The number of bytes that have been consumed by {@code
+ * onSampleDataFound(int, MediaParser.InputReader)} for the specified track, since the
+ * last byte belonging to the sample whose metadata is being passed.
* @param cryptoData Encryption data required to decrypt the sample. May be null for
* unencrypted samples.
*/
void onSampleCompleted(
int trackIndex,
- long timeUs,
+ long timeMicros,
int flags,
int size,
int offset,
@@ -431,63 +432,75 @@
}
}
+ /** Thrown when an error occurs while parsing a media stream. */
+ public static final class ParsingException extends IOException {
+
+ private ParsingException(ParserException cause) {
+ super(cause);
+ }
+ }
+
// Public constants.
/**
- * Sets whether constant bitrate seeking should be enabled for exo.AdtsParser. {@code boolean}
+ * Sets whether constant bitrate seeking should be enabled for ADTS parsing. {@code boolean}
* expected. Default value is {@code false}.
*/
public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING =
- "exo.AdtsParser.enableCbrSeeking";
+ "android.media.mediaparser.adts.enableCbrSeeking";
/**
- * Sets whether constant bitrate seeking should be enabled for exo.AmrParser. {@code boolean}
+ * Sets whether constant bitrate seeking should be enabled for AMR. {@code boolean} expected.
+ * Default value is {@code false}.
+ */
+ public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING =
+ "android.media.mediaparser.amr.enableCbrSeeking";
+ /**
+ * Sets whether the ID3 track should be disabled for FLAC. {@code boolean} expected. Default
+ * value is {@code false}.
+ */
+ public static final String PARAMETER_FLAC_DISABLE_ID3 =
+ "android.media.mediaparser.flac.disableId3";
+ /**
+ * Sets whether MP4 parsing should ignore edit lists. {@code boolean} expected. Default value is
+ * {@code false}.
+ */
+ public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS =
+ "android.media.mediaparser.mp4.ignoreEditLists";
+ /**
+ * Sets whether MP4 parsing should ignore the tfdt box. {@code boolean} expected. Default value
+ * is {@code false}.
+ */
+ public static final String PARAMETER_MP4_IGNORE_TFDT_BOX =
+ "android.media.mediaparser.mp4.ignoreTfdtBox";
+ /**
+ * Sets whether MP4 parsing should treat all video frames as key frames. {@code boolean}
* expected. Default value is {@code false}.
*/
- public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "exo.AmrParser.enableCbrSeeking";
+ public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES =
+ "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes";
/**
- * Sets whether the ID3 track should be disabled for exo.FlacParser. {@code boolean} expected.
- * Default value is {@code false}.
- */
- public static final String PARAMETER_FLAC_DISABLE_ID3 = "exo.FlacParser.disableId3";
- /**
- * Sets whether exo.FragmentedMp4Parser should ignore edit lists. {@code boolean} expected.
- * Default value is {@code false}.
- */
- public static final String PARAMETER_FMP4_IGNORE_EDIT_LISTS =
- "exo.FragmentedMp4Parser.ignoreEditLists";
- /**
- * Sets whether exo.FragmentedMp4Parser should ignore the tfdt box. {@code boolean} expected.
- * Default value is {@code false}.
- */
- public static final String PARAMETER_FMP4_IGNORE_TFDT_BOX =
- "exo.FragmentedMp4Parser.ignoreTfdtBox";
- /**
- * Sets whether exo.FragmentedMp4Parser should treat all video frames as key frames. {@code
- * boolean} expected. Default value is {@code false}.
- */
- public static final String PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES =
- "exo.FragmentedMp4Parser.treatVideoFramesAsKeyframes";
- /**
- * Sets whether exo.MatroskaParser should avoid seeking to the cues element. {@code boolean}
+ * Sets whether Matroska parsing should avoid seeking to the cues element. {@code boolean}
* expected. Default value is {@code false}.
*
* <p>If this flag is enabled and the cues element occurs after the first cluster, then the
* media is treated as unseekable.
*/
public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING =
- "exo.MatroskaParser.disableCuesSeeking";
+ "android.media.mediaparser.matroska.disableCuesSeeking";
/**
- * Sets whether the ID3 track should be disabled for exo.Mp3Parser. {@code boolean} expected.
+ * Sets whether the ID3 track should be disabled for MP3. {@code boolean} expected. Default
+ * value is {@code false}.
+ */
+ public static final String PARAMETER_MP3_DISABLE_ID3 =
+ "android.media.mediaparser.mp3.disableId3";
+ /**
+ * Sets whether constant bitrate seeking should be enabled for MP3. {@code boolean} expected.
* Default value is {@code false}.
*/
- public static final String PARAMETER_MP3_DISABLE_ID3 = "exo.Mp3Parser.disableId3";
+ public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING =
+ "android.media.mediaparser.mp3.enableCbrSeeking";
/**
- * Sets whether constant bitrate seeking should be enabled for exo.Mp3Parser. {@code boolean}
- * expected. Default value is {@code false}.
- */
- public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "exo.Mp3Parser.enableCbrSeeking";
- /**
- * Sets whether exo.Mp3Parser should generate a time-to-byte mapping. {@code boolean} expected.
+ * Sets whether MP3 parsing should generate a time-to-byte mapping. {@code boolean} expected.
* Default value is {@code false}.
*
* <p>Enabling this flag may require to scan a significant portion of the file to compute a seek
@@ -500,18 +513,13 @@
* </ul>
*/
public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING =
- "exo.Mp3Parser.enableIndexSeeking";
+ "android.media.mediaparser.mp3.enableIndexSeeking";
/**
- * Sets whether exo.Mp4Parser should ignore edit lists. {@code boolean} expected. Default value
- * is {@code false}.
- */
- public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "exo.Mp4Parser.ignoreEditLists";
- /**
- * Sets the operation mode for exo.TsParser. {@code String} expected. Valid values are {@code
+ * Sets the operation mode for TS parsing. {@code String} expected. Valid values are {@code
* "single_pmt"}, {@code "multi_pmt"}, and {@code "hls"}. Default value is {@code "single_pmt"}.
*
- * <p>The operation modes alter the way exo.TsParser behaves so that it can handle certain kinds
- * of commonly-occurring malformed media.
+ * <p>The operation modes alter the way TS behaves so that it can handle certain kinds of
+ * commonly-occurring malformed media.
*
* <ul>
* <li>{@code "single_pmt"}: Only the first found PMT is parsed. Others are ignored, even if
@@ -520,47 +528,48 @@
* <li>{@code "hls"}: Enable {@code "single_pmt"} mode, and ignore continuity counters.
* </ul>
*/
- public static final String PARAMETER_TS_MODE = "exo.TsParser.mode";
+ public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode";
/**
- * Sets whether exo.TsParser should treat samples consisting of non-IDR I slices as
- * synchronization samples (key-frames). {@code boolean} expected. Default value is {@code
- * false}.
+ * Sets whether TS should treat samples consisting of non-IDR I slices as synchronization
+ * samples (key-frames). {@code boolean} expected. Default value is {@code false}.
*/
public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES =
- "exo.TsParser.allowNonIdrAvcKeyframes";
+ "android.media.mediaparser.ts.allowNonIdrAvcKeyframes";
/**
- * Sets whether exo.TsParser should ignore AAC elementary streams. {@code boolean} expected.
+ * Sets whether TS parsing should ignore AAC elementary streams. {@code boolean} expected.
* Default value is {@code false}.
*/
- public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "exo.TsParser.ignoreAacStream";
+ public static final String PARAMETER_TS_IGNORE_AAC_STREAM =
+ "android.media.mediaparser.ts.ignoreAacStream";
/**
- * Sets whether exo.TsParser should ignore AVC elementary streams. {@code boolean} expected.
+ * Sets whether TS parsing should ignore AVC elementary streams. {@code boolean} expected.
* Default value is {@code false}.
*/
- public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "exo.TsParser.ignoreAvcStream";
+ public static final String PARAMETER_TS_IGNORE_AVC_STREAM =
+ "android.media.mediaparser.ts.ignoreAvcStream";
/**
- * Sets whether exo.TsParser should ignore splice information streams. {@code boolean} expected.
+ * Sets whether TS parsing should ignore splice information streams. {@code boolean} expected.
* Default value is {@code false}.
*/
public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM =
- "exo.TsParser.ignoreSpliceInfoStream";
+ "android.media.mediaparser.ts.ignoreSpliceInfoStream";
/**
- * Sets whether exo.TsParser should split AVC stream into access units based on slice headers.
+ * Sets whether TS parsing should split AVC stream into access units based on slice headers.
* {@code boolean} expected. Default value is {@code false}.
*
* <p>This flag should be left disabled if the stream contains access units delimiters in order
* to avoid unnecessary computational costs.
*/
public static final String PARAMETER_TS_DETECT_ACCESS_UNITS =
- "exo.TsParser.ignoreDetectAccessUnits";
+ "android.media.mediaparser.ts.ignoreDetectAccessUnits";
/**
- * Sets whether exo.TsParser should handle HDMV DTS audio streams. {@code boolean} expected.
+ * Sets whether TS parsing should handle HDMV DTS audio streams. {@code boolean} expected.
* Default value is {@code false}.
*
* <p>Enabling this flag will disable the detection of SCTE subtitles.
*/
public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS =
- "exo.TsParser.enableHdmvDtsAudioStreams";
+ "android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
// Private constants.
@@ -632,7 +641,7 @@
private Extractor mExtractor;
private ExtractorInput mExtractorInput;
private long mPendingSeekPosition;
- private long mPendingSeekTimeUs;
+ private long mPendingSeekTimeMicros;
// Public methods.
@@ -760,7 +769,7 @@
}
if (isPendingSeek()) {
- mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeUs);
+ mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeMicros);
removePendingSeek();
}
@@ -768,6 +777,8 @@
int result = 0;
try {
result = mExtractor.read(mExtractorInput, mPositionHolder);
+ } catch (ParserException e) {
+ throw new ParsingException(e);
} catch (InterruptedException e) {
// TODO: Remove this exception replacement once we update the ExoPlayer version.
throw new InterruptedIOException();
@@ -786,7 +797,7 @@
* Seeks within the media container being extracted.
*
* <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link
- * OutputConsumer#onSeekMap(SeekMap)}.
+ * OutputConsumer#onSeekMapFound(SeekMap)}.
*
* <p>Following a call to this method, the {@link InputReader} passed to the next invocation of
* {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream.
@@ -796,9 +807,9 @@
public void seek(@NonNull SeekPoint seekPoint) {
if (mExtractor == null) {
mPendingSeekPosition = seekPoint.position;
- mPendingSeekTimeUs = seekPoint.timeUs;
+ mPendingSeekTimeMicros = seekPoint.timeMicros;
} else {
- mExtractor.seek(seekPoint.position, seekPoint.timeUs);
+ mExtractor.seek(seekPoint.position, seekPoint.timeMicros);
}
}
@@ -836,7 +847,7 @@
private void removePendingSeek() {
mPendingSeekPosition = -1;
- mPendingSeekTimeUs = -1;
+ mPendingSeekTimeMicros = -1;
}
// Private classes.
@@ -897,12 +908,12 @@
@Override
public void endTracks() {
- mOutputConsumer.onTracksFound(mTrackOutputAdapters.size());
+ mOutputConsumer.onTrackCountFound(mTrackOutputAdapters.size());
}
@Override
public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
- mOutputConsumer.onSeekMap(new SeekMap(exoplayerSeekMap));
+ mOutputConsumer.onSeekMapFound(new SeekMap(exoplayerSeekMap));
}
}
@@ -916,7 +927,7 @@
@Override
public void format(Format format) {
- mOutputConsumer.onTrackData(
+ mOutputConsumer.onTrackDataFound(
mTrackIndex,
new TrackData(
toMediaFormat(format), toFrameworkDrmInitData(format.drmInitData)));
@@ -927,7 +938,7 @@
throws IOException {
mScratchExtractorInputAdapter.setExtractorInput(input, length);
long positionBeforeReading = mScratchExtractorInputAdapter.getPosition();
- mOutputConsumer.onSampleData(mTrackIndex, mScratchExtractorInputAdapter);
+ mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchExtractorInputAdapter);
return (int) (mScratchExtractorInputAdapter.getPosition() - positionBeforeReading);
}
@@ -935,7 +946,7 @@
public void sampleData(ParsableByteArray data, int length) {
mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
try {
- mOutputConsumer.onSampleData(mTrackIndex, mScratchParsableByteArrayAdapter);
+ mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchParsableByteArrayAdapter);
} catch (IOException e) {
// Unexpected.
throw new RuntimeException(e);
@@ -1174,15 +1185,14 @@
expectedTypeByParameterName.put(PARAMETER_ADTS_ENABLE_CBR_SEEKING, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_AMR_ENABLE_CBR_SEEKING, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_FLAC_DISABLE_ID3, Boolean.class);
- expectedTypeByParameterName.put(PARAMETER_FMP4_IGNORE_EDIT_LISTS, Boolean.class);
- expectedTypeByParameterName.put(PARAMETER_FMP4_IGNORE_TFDT_BOX, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_TFDT_BOX, Boolean.class);
expectedTypeByParameterName.put(
- PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class);
+ PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_MP3_DISABLE_ID3, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_CBR_SEEKING, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_INDEX_SEEKING, Boolean.class);
- expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_TS_MODE, String.class);
expectedTypeByParameterName.put(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AAC_STREAM, Boolean.class);
diff --git a/apex/permission/framework/api/current.txt b/apex/permission/framework/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/permission/framework/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/permission/framework/api/module-lib-current.txt b/apex/permission/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/permission/framework/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/permission/framework/api/module-lib-removed.txt b/apex/permission/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/permission/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/permission/framework/api/removed.txt b/apex/permission/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/permission/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/permission/framework/api/system-current.txt b/apex/permission/framework/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/permission/framework/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/permission/framework/api/system-removed.txt b/apex/permission/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/permission/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/current.txt b/apex/sdkextensions/framework/api/current.txt
new file mode 100644
index 0000000..9041262
--- /dev/null
+++ b/apex/sdkextensions/framework/api/current.txt
@@ -0,0 +1,10 @@
+// Signature format: 2.0
+package android.os.ext.test {
+
+ @Deprecated public class Test {
+ ctor @Deprecated public Test();
+ method @Deprecated public void testE();
+ }
+
+}
+
diff --git a/apex/sdkextensions/framework/api/module-lib-current.txt b/apex/sdkextensions/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..494c12f
--- /dev/null
+++ b/apex/sdkextensions/framework/api/module-lib-current.txt
@@ -0,0 +1,9 @@
+// Signature format: 2.0
+package android.os.ext.test {
+
+ @Deprecated public class Test {
+ method @Deprecated public void testD();
+ }
+
+}
+
diff --git a/apex/sdkextensions/framework/api/module-lib-removed.txt b/apex/sdkextensions/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/sdkextensions/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/removed.txt b/apex/sdkextensions/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/sdkextensions/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/system-current.txt b/apex/sdkextensions/framework/api/system-current.txt
new file mode 100644
index 0000000..056eb41
--- /dev/null
+++ b/apex/sdkextensions/framework/api/system-current.txt
@@ -0,0 +1,18 @@
+// Signature format: 2.0
+package android.os.ext {
+
+ public class SdkExtensions {
+ method public static int getExtensionVersion(int);
+ }
+
+}
+
+package android.os.ext.test {
+
+ @Deprecated public class Test {
+ method @Deprecated public void testF();
+ method @Deprecated public void testG();
+ }
+
+}
+
diff --git a/apex/sdkextensions/framework/api/system-removed.txt b/apex/sdkextensions/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/sdkextensions/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 8185bb0..986682e 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -166,10 +166,8 @@
android_test {
name: "FrameworkStatsdTest",
- platform_apis: true,
+ sdk_version: "module_current",
srcs: [
- // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
- ":framework-statsd-sources",
"test/**/*.java",
],
manifest: "test/AndroidManifest.xml",
@@ -180,6 +178,7 @@
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
+ "framework-statsd",
],
test_suites: [
"device-tests",
diff --git a/apex/statsd/framework/api/current.txt b/apex/statsd/framework/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/statsd/framework/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/statsd/framework/api/module-lib-current.txt b/apex/statsd/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..8b6e217
--- /dev/null
+++ b/apex/statsd/framework/api/module-lib-current.txt
@@ -0,0 +1,10 @@
+// Signature format: 2.0
+package android.os {
+
+ public class StatsFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
+ }
+
+}
+
diff --git a/apex/statsd/framework/api/module-lib-removed.txt b/apex/statsd/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/statsd/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/statsd/framework/api/removed.txt b/apex/statsd/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/statsd/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/statsd/framework/api/system-current.txt b/apex/statsd/framework/api/system-current.txt
new file mode 100644
index 0000000..3ea5724
--- /dev/null
+++ b/apex/statsd/framework/api/system-current.txt
@@ -0,0 +1,111 @@
+// Signature format: 2.0
+package android.app {
+
+ public final class StatsManager {
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void addConfig(long, byte[]) throws android.app.StatsManager.StatsUnavailableException;
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean addConfiguration(long, byte[]);
+ method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void clearPullAtomCallback(int);
+ method @Deprecated @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getData(long);
+ method @Deprecated @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getMetadata();
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws android.app.StatsManager.StatsUnavailableException;
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException;
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException;
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException;
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent);
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException;
+ method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void setPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback);
+ field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
+ field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS";
+ field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
+ field public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
+ field public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
+ field public static final String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE";
+ field public static final String EXTRA_STATS_SUBSCRIPTION_ID = "android.app.extra.STATS_SUBSCRIPTION_ID";
+ field public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
+ field public static final int PULL_SKIP = 1; // 0x1
+ field public static final int PULL_SUCCESS = 0; // 0x0
+ }
+
+ public static class StatsManager.PullAtomMetadata {
+ method @Nullable public int[] getAdditiveFields();
+ method public long getCoolDownMillis();
+ method public long getTimeoutMillis();
+ }
+
+ public static class StatsManager.PullAtomMetadata.Builder {
+ ctor public StatsManager.PullAtomMetadata.Builder();
+ method @NonNull public android.app.StatsManager.PullAtomMetadata build();
+ method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setAdditiveFields(@NonNull int[]);
+ method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setCoolDownMillis(long);
+ method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setTimeoutMillis(long);
+ }
+
+ public static interface StatsManager.StatsPullAtomCallback {
+ method public int onPullAtom(int, @NonNull java.util.List<android.util.StatsEvent>);
+ }
+
+ public static class StatsManager.StatsUnavailableException extends android.util.AndroidException {
+ ctor public StatsManager.StatsUnavailableException(String);
+ ctor public StatsManager.StatsUnavailableException(String, Throwable);
+ }
+
+}
+
+package android.os {
+
+ public final class StatsDimensionsValue implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getBooleanValue();
+ method public int getField();
+ method public float getFloatValue();
+ method public int getIntValue();
+ method public long getLongValue();
+ method public String getStringValue();
+ method public java.util.List<android.os.StatsDimensionsValue> getTupleValueList();
+ method public int getValueType();
+ method public boolean isValueType(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int BOOLEAN_VALUE_TYPE = 5; // 0x5
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.StatsDimensionsValue> CREATOR;
+ field public static final int FLOAT_VALUE_TYPE = 6; // 0x6
+ field public static final int INT_VALUE_TYPE = 3; // 0x3
+ field public static final int LONG_VALUE_TYPE = 4; // 0x4
+ field public static final int STRING_VALUE_TYPE = 2; // 0x2
+ field public static final int TUPLE_VALUE_TYPE = 7; // 0x7
+ }
+
+}
+
+package android.util {
+
+ public final class StatsEvent {
+ method @NonNull public static android.util.StatsEvent.Builder newBuilder();
+ }
+
+ public static final class StatsEvent.Builder {
+ method @NonNull public android.util.StatsEvent.Builder addBooleanAnnotation(byte, boolean);
+ method @NonNull public android.util.StatsEvent.Builder addIntAnnotation(byte, int);
+ method @NonNull public android.util.StatsEvent build();
+ method @NonNull public android.util.StatsEvent.Builder setAtomId(int);
+ method @NonNull public android.util.StatsEvent.Builder usePooledBuffer();
+ method @NonNull public android.util.StatsEvent.Builder writeAttributionChain(@NonNull int[], @NonNull String[]);
+ method @NonNull public android.util.StatsEvent.Builder writeBoolean(boolean);
+ method @NonNull public android.util.StatsEvent.Builder writeByteArray(@NonNull byte[]);
+ method @NonNull public android.util.StatsEvent.Builder writeFloat(float);
+ method @NonNull public android.util.StatsEvent.Builder writeInt(int);
+ method @NonNull public android.util.StatsEvent.Builder writeKeyValuePairs(@Nullable android.util.SparseIntArray, @Nullable android.util.SparseLongArray, @Nullable android.util.SparseArray<java.lang.String>, @Nullable android.util.SparseArray<java.lang.Float>);
+ method @NonNull public android.util.StatsEvent.Builder writeLong(long);
+ method @NonNull public android.util.StatsEvent.Builder writeString(@NonNull String);
+ }
+
+ public final class StatsLog {
+ method public static void write(@NonNull android.util.StatsEvent);
+ method public static void writeRaw(@NonNull byte[], int);
+ }
+
+}
+
diff --git a/apex/statsd/framework/api/system-removed.txt b/apex/statsd/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/statsd/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/current.txt b/api/current.txt
index d944d24..3d73427 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -290,6 +290,7 @@
field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
+ field public static final int allowDontAutoRevokePermissions = 16844309; // 0x1010615
field public static final int allowEmbedded = 16843765; // 0x10103f5
field public static final int allowNativeHeapPointerTagging = 16844307; // 0x1010613
field public static final int allowParallelSyncs = 16843570; // 0x1010332
@@ -572,6 +573,7 @@
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
+ field public static final int enableGwpAsan = 16844312; // 0x1010618
field public static final int enableVrMode = 16844069; // 0x1010525
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
@@ -620,7 +622,6 @@
field public static final int fastScrollTextColor = 16843609; // 0x1010359
field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336
field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339
- field public static final int featureId = 16844301; // 0x101060d
field public static final int fillAfter = 16843197; // 0x10101bd
field public static final int fillAlpha = 16843980; // 0x10104cc
field public static final int fillBefore = 16843196; // 0x10101bc
@@ -953,7 +954,7 @@
field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
field public static final int mediaRouteTypes = 16843694; // 0x10103ae
field public static final int menuCategory = 16843230; // 0x10101de
- field public static final int mimeGroup = 16844309; // 0x1010615
+ field public static final int mimeGroup = 16844311; // 0x1010617
field public static final int mimeType = 16842790; // 0x1010026
field public static final int min = 16844089; // 0x1010539
field public static final int minAspectRatio = 16844187; // 0x101059b
@@ -1082,7 +1083,7 @@
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
- field public static final int preserveLegacyExternalStorage = 16844308; // 0x1010614
+ field public static final int preserveLegacyExternalStorage = 16844310; // 0x1010616
field public static final int previewImage = 16843482; // 0x10102da
field public static final int primaryContentAlpha = 16844114; // 0x1010552
field public static final int priority = 16842780; // 0x101001c
@@ -1139,6 +1140,7 @@
field public static final int reqKeyboardType = 16843304; // 0x1010228
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
+ field public static final int requestDontAutoRevokePermissions = 16844308; // 0x1010614
field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
@@ -4586,7 +4588,7 @@
public final class AsyncNotedAppOp implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public String getFeatureId();
+ method @Nullable public String getAttributionTag();
method @NonNull public String getMessage();
method @IntRange(from=0) public int getNotingUid();
method @NonNull public String getOp();
@@ -5993,7 +5995,7 @@
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.app.AutomaticZenRule getAutomaticZenRule(String);
method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
- method @Nullable public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
+ method @NonNull public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
method public final int getCurrentInterruptionFilter();
method public int getImportance();
method public android.app.NotificationChannel getNotificationChannel(String);
@@ -6430,7 +6432,7 @@
public final class SyncNotedAppOp implements android.os.Parcelable {
ctor public SyncNotedAppOp(@IntRange(from=0L) int, @Nullable String);
method public int describeContents();
- method @Nullable public String getFeatureId();
+ method @Nullable public String getAttributionTag();
method @NonNull public String getOp();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.SyncNotedAppOp> CREATOR;
@@ -6856,9 +6858,9 @@
method @Nullable public String getAlwaysOnVpnPackage(@NonNull android.content.ComponentName);
method @NonNull @WorkerThread public android.os.Bundle getApplicationRestrictions(@Nullable android.content.ComponentName, String);
method @Deprecated @Nullable public String getApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName);
- method public boolean getAutoTime(@NonNull android.content.ComponentName);
+ method public boolean getAutoTimeEnabled(@NonNull android.content.ComponentName);
method @Deprecated public boolean getAutoTimeRequired();
- method public boolean getAutoTimeZone(@NonNull android.content.ComponentName);
+ method public boolean getAutoTimeZoneEnabled(@NonNull android.content.ComponentName);
method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName);
method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName);
method public boolean getCameraDisabled(@Nullable android.content.ComponentName);
@@ -6910,7 +6912,6 @@
method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getPermittedInputMethods(@NonNull android.content.ComponentName);
method public int getPersonalAppsSuspendedReasons(@NonNull android.content.ComponentName);
- method @NonNull public java.util.List<java.lang.String> getProtectedPackages(@NonNull android.content.ComponentName);
method public long getRequiredStrongAuthTimeout(@Nullable android.content.ComponentName);
method public boolean getScreenCaptureDisabled(@Nullable android.content.ComponentName);
method public java.util.List<android.os.UserHandle> getSecondaryUsers(@NonNull android.content.ComponentName);
@@ -6921,6 +6922,7 @@
method @Nullable public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method @Nullable public android.os.PersistableBundle getTransferOwnershipBundle();
method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName);
+ method @NonNull public java.util.List<java.lang.String> getUserControlDisabledPackages(@NonNull android.content.ComponentName);
method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
@@ -6940,7 +6942,7 @@
method public boolean isApplicationHidden(@NonNull android.content.ComponentName, String);
method public boolean isBackupServiceEnabled(@NonNull android.content.ComponentName);
method @Deprecated public boolean isCallerApplicationRestrictionsManagingPackage();
- method public boolean isCommonCriteriaModeEnabled(@NonNull android.content.ComponentName);
+ method public boolean isCommonCriteriaModeEnabled(@Nullable android.content.ComponentName);
method public boolean isDeviceIdAttestationSupported();
method public boolean isDeviceOwnerApp(String);
method public boolean isEphemeralUser(@NonNull android.content.ComponentName);
@@ -6982,9 +6984,9 @@
method public boolean setApplicationHidden(@NonNull android.content.ComponentName, String, boolean);
method @WorkerThread public void setApplicationRestrictions(@Nullable android.content.ComponentName, String, android.os.Bundle);
method @Deprecated public void setApplicationRestrictionsManagingPackage(@NonNull android.content.ComponentName, @Nullable String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void setAutoTime(@NonNull android.content.ComponentName, boolean);
+ method public void setAutoTimeEnabled(@NonNull android.content.ComponentName, boolean);
method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean);
- method public void setAutoTimeZone(@NonNull android.content.ComponentName, boolean);
+ method public void setAutoTimeZoneEnabled(@NonNull android.content.ComponentName, boolean);
method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean);
@@ -7040,7 +7042,6 @@
method public void setPersonalAppsSuspended(@NonNull android.content.ComponentName, boolean);
method public void setProfileEnabled(@NonNull android.content.ComponentName);
method public void setProfileName(@NonNull android.content.ComponentName, String);
- method public void setProtectedPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setRecommendedGlobalProxy(@NonNull android.content.ComponentName, @Nullable android.net.ProxyInfo);
method public void setRequiredStrongAuthTimeout(@NonNull android.content.ComponentName, long);
method public boolean setResetPasswordToken(android.content.ComponentName, byte[]);
@@ -7059,6 +7060,7 @@
method public boolean setTimeZone(@NonNull android.content.ComponentName, String);
method public void setTrustAgentConfiguration(@NonNull android.content.ComponentName, @NonNull android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(@Nullable android.content.ComponentName, String, boolean);
+ method public void setUserControlDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setUserIcon(@NonNull android.content.ComponentName, android.graphics.Bitmap);
method public int startUserInBackground(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public int stopUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
@@ -9696,7 +9698,7 @@
method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]);
method public int delete(@NonNull android.net.Uri, @Nullable android.os.Bundle);
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
- method @Nullable public final String getCallingFeatureId();
+ method @Nullable public final String getCallingAttributionTag();
method @Nullable public final String getCallingPackage();
method @Nullable public final String getCallingPackageUnchecked();
method @Nullable public final android.content.Context getContext();
@@ -10030,11 +10032,11 @@
method @CheckResult(suggest="#enforceUriPermission(Uri,int,int,String)") public abstract int checkUriPermission(android.net.Uri, int, int, int);
method @CheckResult(suggest="#enforceUriPermission(Uri,String,String,int,int,int,String)") public abstract int checkUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int);
method @Deprecated public abstract void clearWallpaper() throws java.io.IOException;
+ method @NonNull public android.content.Context createAttributionContext(@Nullable String);
method public abstract android.content.Context createConfigurationContext(@NonNull android.content.res.Configuration);
method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.Context createDeviceProtectedStorageContext();
method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
- method @NonNull public android.content.Context createFeatureContext(@Nullable String);
method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.content.Context createWindowContext(int, @Nullable android.os.Bundle);
method public abstract String[] databaseList();
@@ -10052,6 +10054,7 @@
method public abstract android.content.Context getApplicationContext();
method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
method public abstract android.content.res.AssetManager getAssets();
+ method @Nullable public String getAttributionTag();
method public abstract java.io.File getCacheDir();
method public abstract ClassLoader getClassLoader();
method public abstract java.io.File getCodeCacheDir();
@@ -10068,7 +10071,6 @@
method @Nullable public abstract java.io.File getExternalFilesDir(@Nullable String);
method public abstract java.io.File[] getExternalFilesDirs(String);
method @Deprecated public abstract java.io.File[] getExternalMediaDirs();
- method @Nullable public String getFeatureId();
method public abstract java.io.File getFileStreamPath(String);
method public abstract java.io.File getFilesDir();
method public java.util.concurrent.Executor getMainExecutor();
@@ -10806,6 +10808,7 @@
field public static final String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE";
field public static final String EXTRA_TEXT = "android.intent.extra.TEXT";
field public static final String EXTRA_TIME = "android.intent.extra.TIME";
+ field public static final String EXTRA_TIMEZONE = "time-zone";
field public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
field public static final String EXTRA_UID = "android.intent.extra.UID";
field public static final String EXTRA_USER = "android.intent.extra.USER";
@@ -11438,6 +11441,7 @@
method public int describeContents();
method public void dump(android.util.Printer, String);
method public static CharSequence getCategoryTitle(android.content.Context, int);
+ method @Nullable public Boolean isGwpAsanEnabled();
method public boolean isProfileableByShell();
method public boolean isResourceOverlay();
method public boolean isVirtualPreload();
@@ -11571,6 +11575,7 @@
method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity);
+ method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle);
method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
}
@@ -11716,6 +11721,7 @@
ctor public LauncherApps.ShortcutQuery();
method public android.content.pm.LauncherApps.ShortcutQuery setActivity(@Nullable android.content.ComponentName);
method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+ method @NonNull public android.content.pm.LauncherApps.ShortcutQuery setLocusIds(@Nullable java.util.List<android.content.LocusId>);
method public android.content.pm.LauncherApps.ShortcutQuery setPackage(@Nullable String);
method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(@Nullable java.util.List<java.lang.String>);
@@ -11891,6 +11897,7 @@
method public void setAppIcon(@Nullable android.graphics.Bitmap);
method public void setAppLabel(@Nullable CharSequence);
method public void setAppPackageName(@Nullable String);
+ method public void setAutoRevokePermissionsMode(boolean);
method public void setInstallLocation(int);
method public void setInstallReason(int);
method public void setMultiPackage();
@@ -12020,6 +12027,8 @@
method public boolean hasSigningCertificate(int, @NonNull byte[], int);
method public abstract boolean hasSystemFeature(@NonNull String);
method public abstract boolean hasSystemFeature(@NonNull String, int);
+ method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean isAutoRevokeWhitelisted(@NonNull String);
+ method public boolean isAutoRevokeWhitelisted();
method public boolean isDefaultApplicationIcon(@NonNull android.graphics.drawable.Drawable);
method public boolean isDeviceUpgrading();
method public abstract boolean isInstantApp();
@@ -12044,6 +12053,7 @@
method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
method public abstract void setApplicationCategoryHint(@NonNull String, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int);
+ method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean setAutoRevokeWhitelisted(@NonNull String, boolean);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
method public abstract void setInstallerPackageName(@NonNull String, @Nullable String);
method public void setMimeGroup(@NonNull String, @NonNull java.util.Set<java.lang.String>);
@@ -17350,7 +17360,7 @@
public final class CameraManager {
method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
method @NonNull public String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
- method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentStreamingCameraIds() throws android.hardware.camera2.CameraAccessException;
+ method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentCameraIds() throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isConcurrentSessionConfigurationSupported(@NonNull java.util.Map<java.lang.String,android.hardware.camera2.params.SessionConfiguration>) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull android.hardware.camera2.CameraDevice.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
@@ -20092,8 +20102,7 @@
method public T numberFormatterSecond(android.icu.number.UnlocalizedNumberFormatter);
}
- public abstract class Precision implements java.lang.Cloneable {
- method public Object clone();
+ public abstract class Precision {
method public static android.icu.number.CurrencyPrecision currency(android.icu.util.Currency.CurrencyUsage);
method public static android.icu.number.FractionPrecision fixedFraction(int);
method public static android.icu.number.Precision fixedSignificantDigits(int);
@@ -20116,8 +20125,7 @@
method public static android.icu.number.Scale powerOfTen(int);
}
- public class ScientificNotation extends android.icu.number.Notation implements java.lang.Cloneable {
- method public Object clone();
+ public class ScientificNotation extends android.icu.number.Notation {
method public android.icu.number.ScientificNotation withExponentSignDisplay(android.icu.number.NumberFormatter.SignDisplay);
method public android.icu.number.ScientificNotation withMinExponentDigits(int);
}
@@ -23593,13 +23601,13 @@
method @FloatRange(from=0, to=63) public double getCn0DbHz();
method @NonNull public String getCodeType();
method public int getConstellationType();
+ method public double getFullInterSignalBiasNanos();
+ method @FloatRange(from=0.0) public double getFullInterSignalBiasUncertaintyNanos();
method public int getMultipathIndicator();
method public double getPseudorangeRateMetersPerSecond();
method public double getPseudorangeRateUncertaintyMetersPerSecond();
method public long getReceivedSvTimeNanos();
method public long getReceivedSvTimeUncertaintyNanos();
- method public double getReceiverInterSignalBiasNanos();
- method @FloatRange(from=0.0) public double getReceiverInterSignalBiasUncertaintyNanos();
method public double getSatelliteInterSignalBiasNanos();
method @FloatRange(from=0.0) public double getSatelliteInterSignalBiasUncertaintyNanos();
method public double getSnrInDb();
@@ -23613,8 +23621,8 @@
method @Deprecated public boolean hasCarrierPhase();
method @Deprecated public boolean hasCarrierPhaseUncertainty();
method public boolean hasCodeType();
- method public boolean hasReceiverInterSignalBiasNanos();
- method public boolean hasReceiverInterSignalBiasUncertaintyNanos();
+ method public boolean hasFullInterSignalBiasNanos();
+ method public boolean hasFullInterSignalBiasUncertaintyNanos();
method public boolean hasSatelliteInterSignalBiasNanos();
method public boolean hasSatelliteInterSignalBiasUncertaintyNanos();
method public boolean hasSnrInDb();
@@ -26420,24 +26428,23 @@
method public void seek(@NonNull android.media.MediaParser.SeekPoint);
method @NonNull public android.media.MediaParser setParameter(@NonNull String, @NonNull Object);
method public boolean supportsParameter(@NonNull String);
- field public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = "exo.AdtsParser.enableCbrSeeking";
- field public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "exo.AmrParser.enableCbrSeeking";
- field public static final String PARAMETER_FLAC_DISABLE_ID3 = "exo.FlacParser.disableId3";
- field public static final String PARAMETER_FMP4_IGNORE_EDIT_LISTS = "exo.FragmentedMp4Parser.ignoreEditLists";
- field public static final String PARAMETER_FMP4_IGNORE_TFDT_BOX = "exo.FragmentedMp4Parser.ignoreTfdtBox";
- field public static final String PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = "exo.FragmentedMp4Parser.treatVideoFramesAsKeyframes";
- field public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = "exo.MatroskaParser.disableCuesSeeking";
- field public static final String PARAMETER_MP3_DISABLE_ID3 = "exo.Mp3Parser.disableId3";
- field public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "exo.Mp3Parser.enableCbrSeeking";
- field public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = "exo.Mp3Parser.enableIndexSeeking";
- field public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "exo.Mp4Parser.ignoreEditLists";
- field public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = "exo.TsParser.allowNonIdrAvcKeyframes";
- field public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = "exo.TsParser.ignoreDetectAccessUnits";
- field public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = "exo.TsParser.enableHdmvDtsAudioStreams";
- field public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "exo.TsParser.ignoreAacStream";
- field public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "exo.TsParser.ignoreAvcStream";
- field public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = "exo.TsParser.ignoreSpliceInfoStream";
- field public static final String PARAMETER_TS_MODE = "exo.TsParser.mode";
+ field public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = "android.media.mediaparser.adts.enableCbrSeeking";
+ field public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "android.media.mediaparser.amr.enableCbrSeeking";
+ field public static final String PARAMETER_FLAC_DISABLE_ID3 = "android.media.mediaparser.flac.disableId3";
+ field public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = "android.media.mediaparser.matroska.disableCuesSeeking";
+ field public static final String PARAMETER_MP3_DISABLE_ID3 = "android.media.mediaparser.mp3.disableId3";
+ field public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "android.media.mediaparser.mp3.enableCbrSeeking";
+ field public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = "android.media.mediaparser.mp3.enableIndexSeeking";
+ field public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "android.media.mediaparser.mp4.ignoreEditLists";
+ field public static final String PARAMETER_MP4_IGNORE_TFDT_BOX = "android.media.mediaparser.mp4.ignoreTfdtBox";
+ field public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes";
+ field public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = "android.media.mediaparser.ts.allowNonIdrAvcKeyframes";
+ field public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = "android.media.mediaparser.ts.ignoreDetectAccessUnits";
+ field public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = "android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
+ field public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "android.media.mediaparser.ts.ignoreAacStream";
+ field public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "android.media.mediaparser.ts.ignoreAvcStream";
+ field public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = "android.media.mediaparser.ts.ignoreSpliceInfoStream";
+ field public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode";
}
public static interface MediaParser.InputReader {
@@ -26448,14 +26455,17 @@
public static interface MediaParser.OutputConsumer {
method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
- method public void onSampleData(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException;
- method public void onSeekMap(@NonNull android.media.MediaParser.SeekMap);
- method public void onTrackData(int, @NonNull android.media.MediaParser.TrackData);
- method public void onTracksFound(int);
+ method public void onSampleDataFound(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException;
+ method public void onSeekMapFound(@NonNull android.media.MediaParser.SeekMap);
+ method public void onTrackCountFound(int);
+ method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData);
+ }
+
+ public static final class MediaParser.ParsingException extends java.io.IOException {
}
public static final class MediaParser.SeekMap {
- method public long getDurationUs();
+ method public long getDurationMicros();
method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
method public boolean isSeekable();
field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000
@@ -26464,7 +26474,7 @@
public static final class MediaParser.SeekPoint {
field @NonNull public static final android.media.MediaParser.SeekPoint START;
field public final long position;
- field public final long timeUs;
+ field public final long timeMicros;
}
public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader {
@@ -27044,7 +27054,7 @@
method public abstract void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo, int);
}
- public class MediaRouter2 {
+ public final class MediaRouter2 {
method @NonNull public java.util.List<android.media.MediaRouter2.RoutingController> getControllers();
method @NonNull public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context);
method @NonNull public java.util.List<android.media.MediaRoute2Info> getRoutes();
@@ -29811,7 +29821,7 @@
public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
- method public void onConnectivityReport(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+ method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
}
@@ -32443,7 +32453,7 @@
method public boolean categoryAllowsForegroundPreference(String);
method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List<java.lang.String> getAidsForPreferredPaymentService();
method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, String);
- method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getDescriptionForPreferredPaymentService();
+ method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public CharSequence getDescriptionForPreferredPaymentService();
method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService();
method public int getSelectionModeForCategory(String);
@@ -37153,12 +37163,10 @@
method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
method @NonNull public android.os.VibrationEffect compose();
field public static final int PRIMITIVE_CLICK = 1; // 0x1
- field public static final int PRIMITIVE_LIGHT_TICK = 7; // 0x7
field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6
field public static final int PRIMITIVE_QUICK_RISE = 4; // 0x4
field public static final int PRIMITIVE_SLOW_RISE = 5; // 0x5
- field public static final int PRIMITIVE_SPIN = 3; // 0x3
- field public static final int PRIMITIVE_THUD = 2; // 0x2
+ field public static final int PRIMITIVE_TICK = 7; // 0x7
}
public abstract class Vibrator {
@@ -40714,6 +40722,7 @@
field public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
field public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
field public static final String RTT_CALLING_MODE = "rtt_calling_mode";
+ field public static final String SECURE_FRP_MODE = "secure_frp_mode";
field public static final String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
field public static final String SETTINGS_CLASSNAME = "settings_classname";
field public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
@@ -43012,12 +43021,12 @@
}
public static final class Dataset.Builder {
- ctor public Dataset.Builder(@NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
ctor public Dataset.Builder(@NonNull android.widget.RemoteViews);
ctor public Dataset.Builder();
method @NonNull public android.service.autofill.Dataset build();
method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender);
method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String);
+ method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
@@ -45973,9 +45982,6 @@
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
- field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
- field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
- field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
field public static final int REMOTE = 3; // 0x3
field public static final int RESTRICTED = 8; // 0x8
@@ -46302,7 +46308,6 @@
field public static final int DURATION_SHORT = 1; // 0x1
field public static final int DURATION_VERY_SHORT = 0; // 0x0
field public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
- field public static final String EXTRA_CALL_CREATED_TIME_MILLIS = "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
@@ -47831,6 +47836,7 @@
method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long);
+ method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
method public void sendTextMessage(@NonNull String, @Nullable String, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent, long);
method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
@@ -48044,13 +48050,13 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
- method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList();
method public static int getActiveDataSubscriptionId();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount();
method public int getActiveSubscriptionInfoCountMax();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+ method @NonNull public java.util.List<android.telephony.SubscriptionInfo> getCompleteActiveSubscriptionInfoList();
method public static int getDefaultDataSubscriptionId();
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
@@ -48207,12 +48213,12 @@
method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
method public boolean isConcurrentVoiceAndDataSupported();
- method public boolean isDataCapable();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported();
method public boolean isNetworkRoaming();
method public boolean isRttSupported();
@@ -48326,7 +48332,6 @@
field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
field public static final int PHONE_TYPE_CDMA = 2; // 0x2
field public static final int PHONE_TYPE_GSM = 1; // 0x1
- field public static final int PHONE_TYPE_IMS = 5; // 0x5
field public static final int PHONE_TYPE_NONE = 0; // 0x0
field public static final int PHONE_TYPE_SIP = 3; // 0x3
field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2
@@ -48996,8 +49001,8 @@
ctor public RegistrationManager.RegistrationCallback();
method public void onRegistered(int);
method public void onRegistering(int);
- method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
- method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+ method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
}
}
@@ -53605,14 +53610,16 @@
public class SurfaceControlViewHost {
ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder);
- method public void addView(@NonNull android.view.View, int, int);
method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage();
+ method @Nullable public android.view.View getView();
method public void relayout(int, int);
method public void release();
+ method public void setView(@NonNull android.view.View, int, int);
}
public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
method public int describeContents();
+ method public void release();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControlViewHost.SurfacePackage> CREATOR;
}
@@ -55566,7 +55573,8 @@
}
public interface WindowInsetsAnimationControlListener {
- method public void onCancelled();
+ method public void onCancelled(@Nullable android.view.WindowInsetsAnimationController);
+ method public void onFinished(@NonNull android.view.WindowInsetsAnimationController);
method public void onReady(@NonNull android.view.WindowInsetsAnimationController, int);
}
@@ -55578,12 +55586,15 @@
method @NonNull public android.graphics.Insets getHiddenStateInsets();
method @NonNull public android.graphics.Insets getShownStateInsets();
method public int getTypes();
+ method public boolean isCancelled();
+ method public boolean isFinished();
+ method public default boolean isReady();
method public void setInsetsAndAlpha(@Nullable android.graphics.Insets, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
}
public interface WindowInsetsController {
method public void addOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
- method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
+ method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @Nullable android.os.CancellationSignal, @NonNull android.view.WindowInsetsAnimationControlListener);
method public int getSystemBarsAppearance();
method public int getSystemBarsBehavior();
method public void hide(int);
@@ -56741,6 +56752,7 @@
method public final void notifySessionResumed();
method public final void notifyViewAppeared(@NonNull android.view.ViewStructure);
method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
+ method public final void notifyViewInsetsChanged(@NonNull android.graphics.Insets);
method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]);
method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext);
@@ -56806,7 +56818,7 @@
public static final class InlinePresentationSpec.Builder {
ctor public InlinePresentationSpec.Builder(@NonNull android.util.Size, @NonNull android.util.Size);
method @NonNull public android.view.inline.InlinePresentationSpec build();
- method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@Nullable android.os.Bundle);
+ method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@NonNull android.os.Bundle);
}
}
@@ -57018,7 +57030,7 @@
ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.view.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(@NonNull android.view.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
- method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
@@ -82233,3 +82245,4 @@
}
}
+
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
index 569e838..83c78fe 100644
--- a/api/lint-baseline.txt
+++ b/api/lint-baseline.txt
@@ -15,6 +15,16 @@
ArrayReturn: android.content.ContentProviderOperation#resolveExtrasBackReferences(android.content.ContentProviderResult[], int) parameter #0:
+ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#SphericalCorrections(double[][], double[][]) parameter #0:
+ Method parameter should be Collection<> (or subclass) instead of raw array; was `double[][]`
+ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#SphericalCorrections(double[][], double[][]) parameter #1:
+ Method parameter should be Collection<> (or subclass) instead of raw array; was `double[][]`
+ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#getCorrectionUncertaintiesArray():
+ Method should return Collection<> (or subclass) instead of raw array; was `double[][]`
+ArrayReturn: android.location.GnssAntennaInfo.SphericalCorrections#getCorrectionsArray():
+ Method should return Collection<> (or subclass) instead of raw array; was `double[][]`
+ArrayReturn: android.service.autofill.FillResponse.Builder#setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews, android.service.autofill.InlinePresentation) parameter #0:
+ Method parameter should be Collection<AutofillId> (or subclass) instead of raw array; was `android.view.autofill.AutofillId[]`
BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
@@ -453,8 +463,12 @@
+ExecutorRegistration: android.media.MediaRouter2#setOnGetControllerHintsListener(android.media.MediaRouter2.OnGetControllerHintsListener):
+ Registration methods should have overload that accepts delivery Executor: `setOnGetControllerHintsListener`
+
+
GenericException: android.content.res.loader.ResourcesProvider#finalize():
- Methods must not throw generic exceptions (`java.lang.Throwable`)
+
HiddenSuperclass: android.content.res.ColorStateList:
@@ -499,6 +513,30 @@
+IntentBuilderName: android.net.VpnManager#provisionVpnProfile(android.net.PlatformVpnProfile):
+ Methods creating an Intent should be named `create<Foo>Intent()`, was `provisionVpnProfile`
+
+
+KotlinOperator: android.media.AudioMetadata.Map#set(android.media.AudioMetadata.Key<T>, T):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: android.media.AudioMetadata.ReadMap#get(android.media.AudioMetadata.Key<T>):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+MethodNameUnits: android.media.MediaParser.SeekMap#getDurationMicros():
+ Returned time values are strongly encouraged to be in milliseconds unless you need the extra precision, was `getDurationMicros`
+
+
+MinMaxConstant: android.telephony.DataFailCause#MAX_ACCESS_PROBE:
+ If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_ACCESS_PROBE
+MinMaxConstant: android.telephony.DataFailCause#MAX_IPV4_CONNECTIONS:
+ If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_IPV4_CONNECTIONS
+MinMaxConstant: android.telephony.DataFailCause#MAX_IPV6_CONNECTIONS:
+ If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_IPV6_CONNECTIONS
+MinMaxConstant: android.telephony.DataFailCause#MAX_PPP_INACTIVITY_TIMER_EXPIRED:
+ If min/max could change in future, make them dynamic methods: android.telephony.DataFailCause#MAX_PPP_INACTIVITY_TIMER_EXPIRED
+
+
MissingNullability: android.app.AsyncNotedAppOp#equals(Object) parameter #0:
MissingNullability: android.app.AsyncNotedAppOp#writeToParcel(android.os.Parcel, int) parameter #0:
@@ -506,11 +544,11 @@
MissingNullability: android.app.SyncNotedAppOp#equals(Object) parameter #0:
MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CHORASMIAN:
- Missing nullability on field `CHORASMIAN` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+
MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G:
- Missing nullability on field `CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+
MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#DIVES_AKURU:
- Missing nullability on field `DIVES_AKURU` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+
MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS:
MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#ELYMAIC:
@@ -556,14 +594,39 @@
MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1:
MissingNullability: android.icu.util.VersionInfo#UNICODE_13_0:
- Missing nullability on field `UNICODE_13_0` in class `class android.icu.util.VersionInfo`
+
MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int, android.media.MediaMetadataRetriever.BitmapParams):
MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int, android.media.MediaMetadataRetriever.BitmapParams):
-
MissingNullability: java.time.chrono.JapaneseEra#REIWA:
- Missing nullability on field `REIWA` in class `class java.time.chrono.JapaneseEra`
+
+
+
+NotCloseable: android.media.MediaCodec.GraphicBlock:
+ Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.media.MediaCodec.GraphicBlock
+NotCloseable: android.media.MediaCodec.LinearBlock:
+ Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.media.MediaCodec.LinearBlock
+NotCloseable: android.media.MediaParser:
+ Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.media.MediaParser
+NotCloseable: android.media.MediaRouter2.RoutingController:
+ Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.media.MediaRouter2.RoutingController
+NotCloseable: android.util.CloseGuard:
+ Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.util.CloseGuard
+NotCloseable: android.view.SurfaceControlViewHost:
+ Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.view.SurfaceControlViewHost
+
+
+OnNameExpected: android.app.admin.DevicePolicyKeyguardService#dismiss():
+ If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+OnNameExpected: android.service.controls.ControlsProviderService#createPublisherFor(java.util.List<java.lang.String>):
+ Methods implemented by developers should follow the on<Something> style, was `createPublisherFor`
+OnNameExpected: android.service.controls.ControlsProviderService#createPublisherForAllAvailable():
+ Methods implemented by developers should follow the on<Something> style, was `createPublisherForAllAvailable`
+OnNameExpected: android.service.controls.ControlsProviderService#createPublisherForSuggested():
+ If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+OnNameExpected: android.service.controls.ControlsProviderService#performControlAction(String, android.service.controls.actions.ControlAction, java.util.function.Consumer<java.lang.Integer>):
+ Methods implemented by developers should follow the on<Something> style, was `performControlAction`
RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
@@ -1189,11 +1252,13 @@
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener):
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener):
+
-
+StreamFiles: android.content.res.loader.DirectoryAssetsProvider#DirectoryAssetsProvider(java.io.File):
+ Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryAssetsProvider(java.io.File)
StreamFiles: android.content.res.loader.DirectoryResourceLoader#DirectoryResourceLoader(java.io.File):
- Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryResourceLoader(java.io.File)
+
Todo: android.hardware.camera2.params.StreamConfigurationMap:
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 69b52693..ee997f1 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -29,7 +29,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
}
- public class TetheringConstants {
+ public final class TetheringConstants {
field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
diff --git a/api/removed.txt b/api/removed.txt
index 8537b21..077c915 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,4 +1,12 @@
// Signature format: 2.0
+package android {
+
+ public static final class R.attr {
+ field public static final int featureId = 16844301; // 0x101060d
+ }
+
+}
+
package android.app {
public class ActivityManager {
@@ -69,11 +77,17 @@
package android.content {
+ public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+ method @Deprecated @Nullable public final String getCallingFeatureId();
+ }
+
public abstract class ContentResolver {
method @Deprecated public void notifyChange(@NonNull Iterable<android.net.Uri>, @Nullable android.database.ContentObserver, int);
}
public abstract class Context {
+ method @Deprecated @NonNull public android.content.Context createFeatureContext(@Nullable String);
+ method @Deprecated @Nullable public String getFeatureId();
method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
method public abstract java.io.File getSharedPreferencesPath(String);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index a8ffc24..3e72f8f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -148,6 +148,7 @@
field public static final String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD";
field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
+ field public static final String NETWORK_STATS_PROVIDER = "android.permission.NETWORK_STATS_PROVIDER";
field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
@@ -205,7 +206,7 @@
field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES";
field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
- field public static final String SECURE_ELEMENT_PRIVILEGED = "android.permission.SECURE_ELEMENT_PRIVILEGED";
+ field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -235,6 +236,7 @@
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
+ field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
field public static final String WIFI_SET_DEVICE_MOBILITY_STATE = "android.permission.WIFI_SET_DEVICE_MOBILITY_STATE";
field public static final String WIFI_UPDATE_USABILITY_STATS_SCORE = "android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE";
@@ -251,9 +253,6 @@
public static final class R.array {
field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
- field public static final int config_restrictedPreinstalledCarrierApps = 17235975; // 0x1070007
- field public static final int config_sms_enabled_locking_shift_tables = 17235977; // 0x1070009
- field public static final int config_sms_enabled_single_shift_tables = 17235976; // 0x1070008
field public static final int simColors = 17235974; // 0x1070006
}
@@ -306,7 +305,6 @@
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemGallery = 17039402; // 0x104002a
- field public static final int low_memory = 17039403; // 0x104002b
}
public static final class R.style {
@@ -353,7 +351,6 @@
method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
method public void unregisterHomeVisibilityObserver(@NonNull android.app.HomeVisibilityObserver);
- method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
}
public static interface ActivityManager.OnUidImportanceListener {
@@ -407,7 +404,6 @@
field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
- field public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
@@ -448,14 +444,37 @@
field public static final int UID_STATE_TOP = 200; // 0xc8
}
- public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable {
+ public static final class AppOpsManager.AttributedHistoricalOps implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public String getFeatureId();
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
method @IntRange(from=0) public int getOpCount();
+ method @Nullable public String getTag();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedHistoricalOps> CREATOR;
+ }
+
+ public static final class AppOpsManager.AttributedOpEntry implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getLastAccessBackgroundTime(int);
+ method public long getLastAccessForegroundTime(int);
+ method public long getLastAccessTime(int);
+ method public long getLastAccessTime(int, int, int);
+ method public long getLastBackgroundDuration(int);
+ method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
+ method public long getLastDuration(int);
+ method public long getLastDuration(int, int, int);
+ method public long getLastForegroundDuration(int);
+ method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
+ method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
+ method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
+ method public long getLastRejectBackgroundTime(int);
+ method public long getLastRejectForegroundTime(int);
+ method public long getLastRejectTime(int);
+ method public long getLastRejectTime(int, int, int);
+ method public boolean isRunning();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedOpEntry> CREATOR;
}
public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
@@ -491,7 +510,7 @@
public static final class AppOpsManager.HistoricalOpsRequest.Builder {
ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
- method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String);
+ method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setAttributionTag(@Nullable String);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
@@ -500,9 +519,9 @@
public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
method public int describeContents();
- method @IntRange(from=0) public int getFeatureCount();
- method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String);
- method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int);
+ method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@NonNull String);
+ method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getAttributedOpsCount();
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
method @IntRange(from=0) public int getOpCount();
@@ -523,8 +542,8 @@
public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.AttributedOpEntry> getAttributedOpEntries();
method @Deprecated public long getDuration();
- method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures();
method public long getLastAccessBackgroundTime(int);
method public long getLastAccessForegroundTime(int);
method public long getLastAccessTime(int);
@@ -554,36 +573,13 @@
public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public String getFeatureId();
+ method @Nullable public String getAttributionTag();
method @Nullable public String getPackageName();
method @IntRange(from=0) public int getUid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR;
}
- public static final class AppOpsManager.OpFeatureEntry implements android.os.Parcelable {
- method public int describeContents();
- method public long getLastAccessBackgroundTime(int);
- method public long getLastAccessForegroundTime(int);
- method public long getLastAccessTime(int);
- method public long getLastAccessTime(int, int, int);
- method public long getLastBackgroundDuration(int);
- method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
- method public long getLastDuration(int);
- method public long getLastDuration(int, int, int);
- method public long getLastForegroundDuration(int);
- method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
- method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
- method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
- method public long getLastRejectBackgroundTime(int);
- method public long getLastRejectForegroundTime(int);
- method public long getLastRejectTime(int);
- method public long getLastRejectTime(int, int, int);
- method public boolean isRunning();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpFeatureEntry> CREATOR;
- }
-
public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
@@ -688,7 +684,7 @@
public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
method public int describeContents();
- method @Nullable public String getFeatureId();
+ method @Nullable public String getAttributionTag();
method @NonNull public String getMessage();
method @NonNull public String getOp();
method @NonNull public String getPackageName();
@@ -757,7 +753,6 @@
public class StatusBarManager {
method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
- method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
}
public static final class StatusBarManager.DisableInfo {
@@ -1371,7 +1366,6 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @Nullable public String getDefaultSmsPackage(int);
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -1416,7 +1410,8 @@
}
public class NetworkStatsManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.net.netstats.provider.NetworkStatsProviderCallback registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.AbstractNetworkStatsProvider);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
}
public static final class UsageEvents.Event {
@@ -2108,10 +2103,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR;
}
- public static class LauncherApps.ShortcutQuery {
- method @NonNull public android.content.pm.LauncherApps.ShortcutQuery setLocusIds(@Nullable java.util.List<android.content.LocusId>);
- }
-
public class PackageInstaller {
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -2133,6 +2124,7 @@
public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
method public boolean getAllocateAggressive();
method @Deprecated public boolean getAllowDowngrade();
+ method public int getAutoRevokePermissionsMode();
method public boolean getDontKillApp();
method public boolean getEnableRollback();
method @Nullable public String[] getGrantedRuntimePermissions();
@@ -2216,7 +2208,7 @@
field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
- field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
+ field public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = -268435456; // 0xf0000000
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000
field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
@@ -2281,7 +2273,6 @@
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
- field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
field public static final int MODULE_APEX_NAME = 1; // 0x1
field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
@@ -4894,16 +4885,10 @@
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
- }
-
- public static interface Tuner.OnResourceLostListener {
- method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
- }
-
- public final class TunerConstants {
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
field public static final int INVALID_STREAM_ID = 65535; // 0xffff
+ field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
field public static final int INVALID_TS_PID = 65535; // 0xffff
field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
field public static final int RESULT_INVALID_STATE = 3; // 0x3
@@ -4917,6 +4902,10 @@
field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0
}
+ public static interface Tuner.OnResourceLostListener {
+ method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
+ }
+
}
package android.media.tv.tuner.dvr {
@@ -5277,7 +5266,6 @@
method public long getSourceTime();
method public long getTimeStamp();
method public int setCurrentTimestamp(long);
- field public static final long TIMESTAMP_UNAVAILABLE = -1L; // 0xffffffffffffffffL
}
public final class TlvFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
@@ -5364,8 +5352,9 @@
field public static final int SIGNAL_TYPE_UNDEFINED = 0; // 0x0
}
- public static class AnalogFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder> {
+ public static class AnalogFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build();
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int);
}
@@ -5429,10 +5418,11 @@
field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
}
- public static class Atsc3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder> {
+ public static class Atsc3FrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setDemodOutputFormat(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setPlpSettings(@NonNull android.media.tv.tuner.frontend.Atsc3PlpSettings[]);
}
@@ -5473,8 +5463,9 @@
field public static final int MODULATION_UNDEFINED = 0; // 0x0
}
- public static class AtscFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AtscFrontendSettings.Builder> {
+ public static class AtscFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings build();
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
}
@@ -5487,7 +5478,7 @@
public class DvbcFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder builder(@NonNull android.content.Context);
method public int getAnnex();
- method public long getFec();
+ method public long getInnerFec();
method public int getModulation();
method public int getOuterFec();
method public int getSpectralInversion();
@@ -5512,10 +5503,11 @@
field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
}
- public static class DvbcFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder> {
+ public static class DvbcFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
- method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFec(long);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setInnerFec(long);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int);
@@ -5590,9 +5582,10 @@
field public static final int VCM_MODE_UNDEFINED = 0; // 0x0
}
- public static class DvbsFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder> {
+ public static class DvbsFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int);
@@ -5619,8 +5612,8 @@
method public int getConstellation();
method public int getGuardInterval();
method public int getHierarchy();
- method public int getHpCodeRate();
- method public int getLpCodeRate();
+ method public int getHighPriorityCodeRate();
+ method public int getLowPriorityCodeRate();
method public int getPlpGroupId();
method public int getPlpId();
method public int getPlpMode();
@@ -5648,20 +5641,20 @@
field public static final int CODERATE_8_9 = 512; // 0x200
field public static final int CODERATE_AUTO = 1; // 0x1
field public static final int CODERATE_UNDEFINED = 0; // 0x0
+ field public static final int CONSTELLATION_16QAM = 4; // 0x4
+ field public static final int CONSTELLATION_256QAM = 16; // 0x10
+ field public static final int CONSTELLATION_64QAM = 8; // 0x8
field public static final int CONSTELLATION_AUTO = 1; // 0x1
- field public static final int CONSTELLATION_CONSTELLATION_16QAM = 4; // 0x4
- field public static final int CONSTELLATION_CONSTELLATION_256QAM = 16; // 0x10
- field public static final int CONSTELLATION_CONSTELLATION_64QAM = 8; // 0x8
- field public static final int CONSTELLATION_CONSTELLATION_QPSK = 2; // 0x2
+ field public static final int CONSTELLATION_QPSK = 2; // 0x2
field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0
+ field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40
+ field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80
+ field public static final int GUARD_INTERVAL_1_128 = 32; // 0x20
+ field public static final int GUARD_INTERVAL_1_16 = 4; // 0x4
+ field public static final int GUARD_INTERVAL_1_32 = 2; // 0x2
+ field public static final int GUARD_INTERVAL_1_4 = 16; // 0x10
+ field public static final int GUARD_INTERVAL_1_8 = 8; // 0x8
field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1
- field public static final int GUARD_INTERVAL_INTERVAL_19_128 = 64; // 0x40
- field public static final int GUARD_INTERVAL_INTERVAL_19_256 = 128; // 0x80
- field public static final int GUARD_INTERVAL_INTERVAL_1_128 = 32; // 0x20
- field public static final int GUARD_INTERVAL_INTERVAL_1_16 = 4; // 0x4
- field public static final int GUARD_INTERVAL_INTERVAL_1_32 = 2; // 0x2
- field public static final int GUARD_INTERVAL_INTERVAL_1_4 = 16; // 0x10
- field public static final int GUARD_INTERVAL_INTERVAL_1_8 = 8; // 0x8
field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0
field public static final int HIERARCHY_1_INDEPTH = 64; // 0x40
field public static final int HIERARCHY_1_NATIVE = 4; // 0x4
@@ -5689,15 +5682,16 @@
field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
}
- public static class DvbtFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder> {
+ public static class DvbtFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setConstellation(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setGuardInterval(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHierarchy(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriority(boolean);
- method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHpCodeRate(int);
- method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setLpCodeRate(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriorityCodeRate(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setLowPriorityCodeRate(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setMiso(boolean);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpGroupId(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpId(int);
@@ -5773,17 +5767,13 @@
field public static final int TYPE_UNDEFINED = 0; // 0x0
}
- public abstract static class FrontendSettings.Builder<T extends android.media.tv.tuner.frontend.FrontendSettings.Builder<T>> {
- method @IntRange(from=1) @NonNull public T setFrequency(int);
- }
-
public class FrontendStatus {
method public int getAgc();
method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpInfo[] getAtsc3PlpInfo();
method public int getBer();
- method public long getFec();
method public int getFreqOffset();
method public int getHierarchy();
+ method public long getInnerFec();
method @NonNull public boolean[] getLayerErrors();
method public int getLnbVoltage();
method public int getMer();
@@ -5868,9 +5858,10 @@
field public static final int ROLLOFF_UNDEFINED = 0; // 0x0
}
- public static class Isdbs3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder> {
+ public static class Isdbs3FrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setCodeRate(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setRolloff(int);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setStreamId(int);
@@ -5910,9 +5901,10 @@
field public static final int STREAM_ID_TYPE_RELATIVE_NUMBER = 1; // 0x1
}
- public static class IsdbsFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder> {
+ public static class IsdbsFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setCodeRate(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setRolloff(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setStreamId(int);
@@ -5955,10 +5947,11 @@
field public static final int MODULATION_UNDEFINED = 0; // 0x0
}
- public static class IsdbtFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder> {
+ public static class IsdbtFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setCodeRate(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setGuardInterval(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setMode(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setModulation(int);
@@ -6089,7 +6082,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
- method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
@@ -6098,7 +6091,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
- method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
@@ -6286,7 +6279,6 @@
method @Nullable public android.net.Network getNetwork();
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
method public void onAutomaticReconnectDisabled();
- method public void onBandwidthUpdateRequested();
method public void onNetworkUnwanted();
method public void onRemoveKeepalivePacketFilter(int);
method public void onSaveAcceptUnvalidated(boolean);
@@ -6300,23 +6292,17 @@
method public void sendNetworkScore(int);
method public void sendSocketKeepaliveEvent(int, int);
method public void setConnected();
- method @Deprecated public void setLegacyExtraInfo(@Nullable String);
- method @Deprecated public void setLegacySubtype(int, @NonNull String);
method public void unregister();
field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
- field public final int providerId;
}
public final class NetworkAgentConfig implements android.os.Parcelable {
method public int describeContents();
method public int getLegacyType();
method @NonNull public String getLegacyTypeName();
- method @Nullable public String getSubscriberId();
method public boolean isExplicitlySelected();
- method public boolean isNat64DetectionEnabled();
method public boolean isPartialConnectivityAcceptable();
- method public boolean isProvisioningNotificationEnabled();
method public boolean isUnvalidatedConnectivityAcceptable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
@@ -6325,18 +6311,14 @@
public static class NetworkAgentConfig.Builder {
ctor public NetworkAgentConfig.Builder();
method @NonNull public android.net.NetworkAgentConfig build();
- method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection();
- method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification();
method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
- method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
}
public final class NetworkCapabilities implements android.os.Parcelable {
- method public boolean deduceRestrictedCapability();
method @NonNull public java.util.List<java.lang.Integer> getAdministratorUids();
method @Nullable public String getSSID();
method @NonNull public int[] getTransportTypes();
@@ -6361,27 +6343,9 @@
field public final android.net.WifiKey wifiKey;
}
- public class NetworkPolicyManager {
- method @NonNull public android.telephony.SubscriptionPlan[] getSubscriptionPlans(int, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
- method public void setSubscriptionOverride(int, int, int, long, @NonNull String);
- method public void setSubscriptionPlans(int, @NonNull android.telephony.SubscriptionPlan[], @NonNull String);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
- field public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 2; // 0x2
- field public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1; // 0x1
- }
-
- public static class NetworkPolicyManager.SubscriptionCallback {
- ctor public NetworkPolicyManager.SubscriptionCallback();
- method public void onSubscriptionOverride(int, int, int);
- method public void onSubscriptionPlansChanged(int, @NonNull android.telephony.SubscriptionPlan[]);
- }
-
public class NetworkProvider {
ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
- method @Nullable public android.os.Messenger getMessenger();
- method @NonNull public String getName();
method public int getProviderId();
method public void onNetworkRequested(@NonNull android.net.NetworkRequest, int, int);
method public void onRequestWithdrawn(@NonNull android.net.NetworkRequest);
@@ -6443,14 +6407,13 @@
public final class NetworkStats implements android.os.Parcelable {
ctor public NetworkStats(long, int);
method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats);
- method @NonNull public android.net.NetworkStats addValues(@NonNull android.net.NetworkStats.Entry);
+ method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry);
method public int describeContents();
method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
field public static final int DEFAULT_NETWORK_NO = 0; // 0x0
field public static final int DEFAULT_NETWORK_YES = 1; // 0x1
- field @Nullable public static final String IFACE_ALL;
field public static final String IFACE_VT = "vt_data0";
field public static final int METERED_NO = 0; // 0x0
field public static final int METERED_YES = 1; // 0x1
@@ -6850,21 +6813,17 @@
package android.net.netstats.provider {
- public abstract class AbstractNetworkStatsProvider {
- ctor public AbstractNetworkStatsProvider();
- method public abstract void requestStatsUpdate(int);
- method public abstract void setAlert(long);
- method public abstract void setLimit(@NonNull String, long);
+ public abstract class NetworkStatsProvider {
+ ctor public NetworkStatsProvider();
+ method public void notifyAlertReached();
+ method public void notifyLimitReached();
+ method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
+ method public abstract void onRequestStatsUpdate(int);
+ method public abstract void onSetAlert(long);
+ method public abstract void onSetLimit(@NonNull String, long);
field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff
}
- public class NetworkStatsProviderCallback {
- method public void onAlertReached();
- method public void onLimitReached();
- method public void onStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
- method public void unregister();
- }
-
}
package android.net.sip {
@@ -7157,7 +7116,7 @@
method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList();
method public int getChannel();
method public int getMaxNumberOfClients();
- method public int getShutdownTimeoutMillis();
+ method public long getShutdownTimeoutMillis();
method public boolean isAutoShutdownEnabled();
method public boolean isClientControlByUserEnabled();
method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration();
@@ -7171,16 +7130,17 @@
ctor public SoftApConfiguration.Builder();
ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
method @NonNull public android.net.wifi.SoftApConfiguration build();
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder enableClientControlByUser(boolean);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAllowedClientList(@NonNull java.util.List<android.net.MacAddress>);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBlockedClientList(@NonNull java.util.List<android.net.MacAddress>);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientList(@NonNull java.util.List<android.net.MacAddress>, @NonNull java.util.List<android.net.MacAddress>);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientControlByUserEnabled(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) long);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
}
@@ -7487,24 +7447,15 @@
}
public final class WifiMigration {
- method @Nullable public static android.net.wifi.WifiMigration.ConfigStoreMigrationData loadFromConfigStore();
+ method @Nullable public static java.io.InputStream convertAndRetrieveSharedConfigStoreFile(int);
+ method @Nullable public static java.io.InputStream convertAndRetrieveUserConfigStoreFile(int, @NonNull android.os.UserHandle);
method @NonNull public static android.net.wifi.WifiMigration.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
- method public static void removeConfigStore();
- }
-
- public static final class WifiMigration.ConfigStoreMigrationData implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
- method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.ConfigStoreMigrationData> CREATOR;
- }
-
- public static final class WifiMigration.ConfigStoreMigrationData.Builder {
- ctor public WifiMigration.ConfigStoreMigrationData.Builder();
- method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData build();
- method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
- method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+ method public static void removeSharedConfigStoreFile(int);
+ method public static void removeUserConfigStoreFile(int, @NonNull android.os.UserHandle);
+ field public static final int STORE_FILE_SHARED_GENERAL = 0; // 0x0
+ field public static final int STORE_FILE_SHARED_SOFTAP = 1; // 0x1
+ field public static final int STORE_FILE_USER_GENERAL = 2; // 0x2
+ field public static final int STORE_FILE_USER_NETWORK_SUGGESTIONS = 3; // 0x3
}
public static final class WifiMigration.SettingsMigrationData implements android.os.Parcelable {
@@ -8223,13 +8174,15 @@
field public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
field public static final String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
field public static final String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
- field public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
+ field @RequiresPermission("android.permission.UPDATE_CONFIG") public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
field public static final String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
field public static final String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
field public static final String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
field public static final String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
field public static final String ACTION_UPDATE_SMART_SELECTION = "android.intent.action.UPDATE_SMART_SELECTION";
field public static final String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES";
+ field public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH";
+ field public static final String EXTRA_VERSION = "android.os.extra.VERSION";
}
public class Environment {
@@ -8939,11 +8892,13 @@
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
- method @BinderThread public void onUpdateUserSensitivePermissionFlags();
+ method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull Runnable);
field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
}
public final class PermissionManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionGrantedPackages();
+ method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionRequestedPackages();
method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledCarrierApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
@@ -9066,18 +9021,6 @@
package android.provider {
- public class BlockedNumberContract {
- field public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
- field public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
- field public static final String RES_BLOCK_STATUS = "block_status";
- field public static final int STATUS_BLOCKED_IN_LIST = 1; // 0x1
- field public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5; // 0x5
- field public static final int STATUS_BLOCKED_PAYPHONE = 4; // 0x4
- field public static final int STATUS_BLOCKED_RESTRICTED = 2; // 0x2
- field public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3; // 0x3
- field public static final int STATUS_NOT_BLOCKED = 0; // 0x0
- }
-
@Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
@@ -9345,7 +9288,6 @@
field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
field public static final String CARRIER_APP_NAMES = "carrier_app_names";
field public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
- field public static final String COMMON_CRITERIA_MODE = "common_criteria_mode";
field public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
field public static final String DEVICE_DEMO_MODE = "device_demo_mode";
field public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED = "device_provisioning_mobile_data";
@@ -9376,7 +9318,6 @@
field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
field public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
- field public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
field public static final String DOZE_ALWAYS_ON = "doze_always_on";
field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
@@ -9389,7 +9330,6 @@
field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
field public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
- field public static final String SECURE_FRP_MODE = "secure_frp_mode";
field public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa
@@ -9407,38 +9347,10 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
}
- public static interface Telephony.CarrierColumns extends android.provider.BaseColumns {
- field @NonNull public static final android.net.Uri CONTENT_URI;
- field public static final String EXPIRATION_TIME = "expiration_time";
- field public static final String KEY_IDENTIFIER = "key_identifier";
- field public static final String KEY_TYPE = "key_type";
- field public static final String LAST_MODIFIED = "last_modified";
- field public static final String MCC = "mcc";
- field public static final String MNC = "mnc";
- field public static final String MVNO_MATCH_DATA = "mvno_match_data";
- field public static final String MVNO_TYPE = "mvno_type";
- field public static final String PUBLIC_KEY = "public_key";
- }
-
- public static final class Telephony.CarrierId.All implements android.provider.BaseColumns {
- field public static final String APN = "apn";
- field @NonNull public static final android.net.Uri CONTENT_URI;
- field public static final String GID1 = "gid1";
- field public static final String GID2 = "gid2";
- field public static final String ICCID_PREFIX = "iccid_prefix";
- field public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern";
- field public static final String MCCMNC = "mccmnc";
- field public static final String PLMN = "plmn";
- field public static final String PRIVILEGE_ACCESS_RULE = "privilege_access_rule";
- field public static final String SPN = "spn";
- }
-
public static final class Telephony.Carriers implements android.provider.BaseColumns {
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
- field @NonNull public static final android.net.Uri DPC_URI;
field public static final String EDITED_STATUS = "edited";
- field public static final int INVALID_APN_ID = -1; // 0xffffffff
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
field public static final String MTU = "mtu";
@@ -9598,7 +9510,7 @@
package android.se.omapi {
public final class Reader {
- method @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED) public boolean reset();
+ method @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION) public boolean reset();
}
}
@@ -9807,7 +9719,7 @@
public static final class Dataset.Builder {
ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
- method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
}
public abstract class InlineSuggestionRenderService extends android.app.Service {
@@ -9968,7 +9880,7 @@
}
public static final class DataLoaderService.FileSystemConnector {
- method public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
}
}
@@ -10646,14 +10558,7 @@
}
public final class PhoneAccount implements android.os.Parcelable {
- field public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 128; // 0x80
- field public static final int CAPABILITY_EMERGENCY_PREFERRED = 8192; // 0x2000
- field public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 512; // 0x200
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
- field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
- field public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
- field public static final String EXTRA_SORT_ORDER = "android.telecom.extra.SORT_ORDER";
- field public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK = "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
}
public static class PhoneAccount.Builder {
@@ -10739,20 +10644,13 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
- field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
- field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
field public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1; // 0x1
field public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; // 0x2
field public static final int CALL_SOURCE_UNSPECIFIED = 0; // 0x0
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
- field public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
- field public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
- field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE";
field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
- field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE";
- field public static final String EXTRA_UNKNOWN_CALL_HANDLE = "android.telecom.extra.UNKNOWN_CALL_HANDLE";
field public static final int TTY_MODE_FULL = 1; // 0x1
field public static final int TTY_MODE_HCO = 2; // 0x2
field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -10789,27 +10687,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
}
- public final class CallForwardingInfo implements android.os.Parcelable {
- ctor public CallForwardingInfo(int, int, @Nullable String, int);
- method public int describeContents();
- method @Nullable public String getNumber();
- method public int getReason();
- method public int getStatus();
- method public int getTimeoutSeconds();
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
- field public static final int REASON_ALL = 4; // 0x4
- field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
- field public static final int REASON_BUSY = 1; // 0x1
- field public static final int REASON_NOT_REACHABLE = 3; // 0x3
- field public static final int REASON_NO_REPLY = 2; // 0x2
- field public static final int REASON_UNCONDITIONAL = 0; // 0x0
- field public static final int STATUS_ACTIVE = 1; // 0x1
- field public static final int STATUS_FDN_CHECK_FAILURE = 2; // 0x2
- field public static final int STATUS_INACTIVE = 0; // 0x0
- field public static final int STATUS_NOT_SUPPORTED = 4; // 0x4
- field public static final int STATUS_UNKNOWN_ERROR = 3; // 0x3
- }
-
public final class CallQuality implements android.os.Parcelable {
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
@@ -11190,7 +11067,6 @@
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
- field @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 512; // 0x200
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -11221,7 +11097,6 @@
}
public final class PreciseDataConnectionState implements android.os.Parcelable {
- ctor public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int, @Nullable android.telephony.data.ApnSetting);
method @Deprecated @NonNull public String getDataConnectionApn();
method @Deprecated public int getDataConnectionApnTypeBitMask();
method @Deprecated public int getDataConnectionFailCause();
@@ -11329,8 +11204,6 @@
public class ServiceState implements android.os.Parcelable {
method public void fillInNotifierBundle(@NonNull android.os.Bundle);
- method public int getDataNetworkType();
- method public int getDataRegistrationState();
method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
@@ -11476,7 +11349,6 @@
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public java.util.List<android.telephony.SmsMessage> getMessagesFromIcc();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
- method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
@@ -11564,9 +11436,7 @@
}
public class TelephonyManager {
- method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int changeIccLockPassword(@NonNull String, @NonNull String);
method public int checkCarrierPrivilegesForPackage(String);
method public int checkCarrierPrivilegesForPackageAnyPhone(String);
method public void dial(String);
@@ -11577,8 +11447,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CallForwardingInfo getCallForwarding(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCallWaitingStatus();
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -11590,20 +11459,16 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
method public String getCdmaPrlVersion();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
method public int getCurrentPhoneType();
method public int getCurrentPhoneType(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
method @Deprecated public boolean getDataEnabled();
method @Deprecated public boolean getDataEnabled(int);
- method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
- method @NonNull public static String getDefaultSimCountryIso();
- method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
- method public int getEmergencyNumberDbVersion();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
method public int getMaxNumberOfSimultaneouslyActiveSims();
@@ -11630,16 +11495,13 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
- method public boolean isModemEnabledForSlot(int);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -11649,7 +11511,6 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
- method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyUserActivity();
@@ -11667,17 +11528,11 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -11716,10 +11571,6 @@
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CALL_WAITING_STATUS_ACTIVE = 1; // 0x1
- field public static final int CALL_WAITING_STATUS_INACTIVE = 2; // 0x2
- field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
- field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
field public static final int CARD_POWER_DOWN = 0; // 0x0
field public static final int CARD_POWER_UP = 1; // 0x1
field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
@@ -11727,10 +11578,6 @@
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
- field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
- field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
- field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
- field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff
field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
@@ -11786,7 +11633,6 @@
field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
- field public static final int PHONE_TYPE_THIRD_PARTY = 4; // 0x4
field public static final int RADIO_POWER_OFF = 0; // 0x0
field public static final int RADIO_POWER_ON = 1; // 0x1
field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -12353,10 +12199,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
- public class ImsManager {
- field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
- }
-
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -12622,82 +12464,12 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
- field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
- field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
- field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
- field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
- field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
- field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
- field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
- field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
- field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
- field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
- field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
- field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
- field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
- field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
- field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
- field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
- field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
- field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
- field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
- field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
- field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
- field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
- field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
- field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
- field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
- field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
- field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
- field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10
- field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
- field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
- field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
- field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
- field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
- field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
- field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
- field public static final int KEY_RTT_ENABLED = 66; // 0x42
- field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
- field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
- field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
- field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
- field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
- field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
- field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
- field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
- field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
- field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
- field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
- field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
- field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
- field public static final int KEY_SMS_FORMAT = 13; // 0xd
- field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
- field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
- field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
- field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
- field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
- field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
- field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
- field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
- field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
- field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
- field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
- field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
- field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
- field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
- field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
- field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
- field public static final int SMS_FORMAT_3GPP = 1; // 0x1
- field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
- field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
- field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
}
public static class ProvisioningManager.Callback {
@@ -12706,56 +12478,6 @@
method public void onProvisioningStringChanged(int, @NonNull String);
}
- public final class RcsContactUceCapability implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
- method @NonNull public android.net.Uri getContactUri();
- method @Nullable public android.net.Uri getServiceUri(long);
- method public boolean isCapable(long);
- method public boolean isCapable(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
- field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
- field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
- field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
- field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
- field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
- field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
- field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
- field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
- field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
- field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
- field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
- field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
- field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
- field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
- field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
- field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
- field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
- field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
- field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
- field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
- field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
- field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
- field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
- field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
- field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
- field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
- field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
- field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
- field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
- field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
- }
-
- public static class RcsContactUceCapability.Builder {
- ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
- method @NonNull public android.telephony.ims.RcsContactUceCapability build();
- }
-
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
@@ -13002,24 +12724,12 @@
method public int transact(android.os.Bundle);
method public int updateCallBarring(int, int, String[]);
method public int updateCallBarringForServiceClass(int, int, String[], int);
- method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
method public int updateCallForward(int, int, String, int, int);
method public int updateCallWaiting(boolean, int);
method public int updateClip(boolean);
method public int updateClir(int);
method public int updateColp(boolean);
method public int updateColr(int);
- field public static final int CALL_BARRING_ALL = 7; // 0x7
- field public static final int CALL_BARRING_ALL_INCOMING = 1; // 0x1
- field public static final int CALL_BARRING_ALL_OUTGOING = 2; // 0x2
- field public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6; // 0x6
- field public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9; // 0x9
- field public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8; // 0x8
- field public static final int CALL_BARRING_OUTGOING_INTL = 3; // 0x3
- field public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4; // 0x4
- field public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10; // 0xa
- field public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5; // 0x5
- field public static final int INVALID_RESULT = -1; // 0xffffffff
}
}
@@ -13224,6 +12934,7 @@
method public long getEventTime();
method @Nullable public android.view.autofill.AutofillId getId();
method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
+ method @Nullable public android.graphics.Insets getInsets();
method @Nullable public CharSequence getText();
method public int getType();
method @Nullable public android.view.contentcapture.ViewNode getViewNode();
@@ -13234,6 +12945,7 @@
field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
+ field public static final int TYPE_VIEW_INSETS_CHANGED = 9; // 0x9
field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 55333cf..10c96a3 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -246,12 +246,6 @@
-ResourceValueFieldName: android.R.array#config_sms_enabled_locking_shift_tables:
- Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_locking_shift_tables`
-ResourceValueFieldName: android.R.array#config_sms_enabled_single_shift_tables:
- Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_single_shift_tables`
-
-
SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
diff --git a/api/test-current.txt b/api/test-current.txt
index 23c3c98..286408d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -263,14 +263,37 @@
field public static final int UID_STATE_TOP = 200; // 0xc8
}
- public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable {
+ public static final class AppOpsManager.AttributedHistoricalOps implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public String getFeatureId();
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
method @IntRange(from=0) public int getOpCount();
+ method @Nullable public String getTag();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedHistoricalOps> CREATOR;
+ }
+
+ public static final class AppOpsManager.AttributedOpEntry implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getLastAccessBackgroundTime(int);
+ method public long getLastAccessForegroundTime(int);
+ method public long getLastAccessTime(int);
+ method public long getLastAccessTime(int, int, int);
+ method public long getLastBackgroundDuration(int);
+ method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
+ method public long getLastDuration(int);
+ method public long getLastDuration(int, int, int);
+ method public long getLastForegroundDuration(int);
+ method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
+ method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
+ method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
+ method public long getLastRejectBackgroundTime(int);
+ method public long getLastRejectForegroundTime(int);
+ method public long getLastRejectTime(int);
+ method public long getLastRejectTime(int, int, int);
+ method public boolean isRunning();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedOpEntry> CREATOR;
}
public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
@@ -311,7 +334,7 @@
public static final class AppOpsManager.HistoricalOpsRequest.Builder {
ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
- method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String);
+ method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setAttributionTag(@Nullable String);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
@@ -320,9 +343,9 @@
public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
method public int describeContents();
- method @IntRange(from=0) public int getFeatureCount();
- method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String);
- method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int);
+ method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@NonNull String);
+ method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getAttributedOpsCount();
method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
method @IntRange(from=0) public int getOpCount();
@@ -343,8 +366,8 @@
public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.AttributedOpEntry> getAttributedOpEntries();
method @Deprecated public long getDuration();
- method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures();
method public long getLastAccessBackgroundTime(int);
method public long getLastAccessForegroundTime(int);
method public long getLastAccessTime(int);
@@ -374,36 +397,13 @@
public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public String getFeatureId();
+ method @Nullable public String getAttributionTag();
method @Nullable public String getPackageName();
method @IntRange(from=0) public int getUid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR;
}
- public static final class AppOpsManager.OpFeatureEntry implements android.os.Parcelable {
- method public int describeContents();
- method public long getLastAccessBackgroundTime(int);
- method public long getLastAccessForegroundTime(int);
- method public long getLastAccessTime(int);
- method public long getLastAccessTime(int, int, int);
- method public long getLastBackgroundDuration(int);
- method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int);
- method public long getLastDuration(int);
- method public long getLastDuration(int, int, int);
- method public long getLastForegroundDuration(int);
- method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int);
- method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int);
- method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int);
- method public long getLastRejectBackgroundTime(int);
- method public long getLastRejectForegroundTime(int);
- method public long getLastRejectTime(int);
- method public long getLastRejectTime(int, int, int);
- method public boolean isRunning();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpFeatureEntry> CREATOR;
- }
-
public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
@@ -465,7 +465,7 @@
public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
method public int describeContents();
- method @Nullable public String getFeatureId();
+ method @Nullable public String getAttributionTag();
method @NonNull public String getMessage();
method @NonNull public String getOp();
method @NonNull public String getPackageName();
@@ -596,9 +596,22 @@
package android.app.blob {
public class BlobStoreManager {
+ method @Nullable public android.app.blob.LeaseInfo getLeaseInfo(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
+ method @NonNull public java.util.List<android.app.blob.BlobHandle> getLeasedBlobs() throws java.io.IOException;
method public void waitForIdle(long) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
}
+ public final class LeaseInfo implements android.os.Parcelable {
+ ctor public LeaseInfo(@NonNull String, long, @IdRes int, @Nullable CharSequence);
+ method public int describeContents();
+ method @Nullable public CharSequence getDescription();
+ method @IdRes public int getDescriptionResId();
+ method public long getExpiryTimeMillis();
+ method @NonNull public String getPackageName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.blob.LeaseInfo> CREATOR;
+ }
+
}
package android.app.prediction {
@@ -843,6 +856,7 @@
method @NonNull public android.content.integrity.RuleSet getCurrentRuleSet();
method @NonNull public String getCurrentRuleSetProvider();
method @NonNull public String getCurrentRuleSetVersion();
+ method @NonNull public java.util.List<java.lang.String> getWhitelistedRuleProviders();
method public void updateRuleSet(@NonNull android.content.integrity.RuleSet, @NonNull android.content.IntentSender);
field public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS";
field public static final int STATUS_FAILURE = 1; // 0x1
@@ -919,6 +933,7 @@
}
public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
+ method public int getAutoRevokePermissionsMode();
method public int getRollbackDataPolicy();
method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions();
}
@@ -1338,8 +1353,8 @@
method @Deprecated public void resetCarrierPhase();
method @Deprecated public void resetCarrierPhaseUncertainty();
method public void resetCodeType();
- method public void resetReceiverInterSignalBiasNanos();
- method public void resetReceiverInterSignalBiasUncertaintyNanos();
+ method public void resetFullInterSignalBiasNanos();
+ method public void resetFullInterSignalBiasUncertaintyNanos();
method public void resetSatelliteInterSignalBiasNanos();
method public void resetSatelliteInterSignalBiasUncertaintyNanos();
method public void resetSnrInDb();
@@ -1356,13 +1371,13 @@
method public void setCn0DbHz(double);
method public void setCodeType(@NonNull String);
method public void setConstellationType(int);
+ method public void setFullInterSignalBiasNanos(double);
+ method public void setFullInterSignalBiasUncertaintyNanos(@FloatRange(from=0.0) double);
method public void setMultipathIndicator(int);
method public void setPseudorangeRateMetersPerSecond(double);
method public void setPseudorangeRateUncertaintyMetersPerSecond(double);
method public void setReceivedSvTimeNanos(long);
method public void setReceivedSvTimeUncertaintyNanos(long);
- method public void setReceiverInterSignalBiasNanos(double);
- method public void setReceiverInterSignalBiasUncertaintyNanos(@FloatRange(from=0.0) double);
method public void setSatelliteInterSignalBiasNanos(double);
method public void setSatelliteInterSignalBiasUncertaintyNanos(@FloatRange(from=0.0) double);
method public void setSnrInDb(double);
@@ -3132,7 +3147,7 @@
public static final class Dataset.Builder {
ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
- method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
}
public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -3417,13 +3432,15 @@
package android.service.quickaccesswallet {
- public interface QuickAccessWalletClient {
+ public interface QuickAccessWalletClient extends java.io.Closeable {
method public void addWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+ method public void addWalletServiceEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
method @NonNull public static android.service.quickaccesswallet.QuickAccessWalletClient create(@NonNull android.content.Context);
method @Nullable public android.content.Intent createWalletIntent();
method @Nullable public android.content.Intent createWalletSettingsIntent();
method public void disconnect();
method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
+ method public void getWalletCards(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
method public boolean isWalletFeatureAvailable();
method public boolean isWalletFeatureAvailableWhenDeviceLocked();
method public boolean isWalletServiceAvailable();
@@ -3718,7 +3735,6 @@
public final class SmsManager {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int checkSmsShortCodeDestination(String, String);
- method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
field public static final int SMS_CATEGORY_FREE_SHORT_CODE = 1; // 0x1
field public static final int SMS_CATEGORY_NOT_SHORT_CODE = 0; // 0x0
field public static final int SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3; // 0x3
@@ -3739,11 +3755,12 @@
public class TelephonyManager {
method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
method public int checkCarrierPrivilegesForPackage(String);
+ method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
method public int getCarrierIdListVersion();
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
- method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
+ method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getDefaultRespondViaMessageApplication();
method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
- method public int getEmergencyNumberDbVersion();
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getEmergencyNumberDbVersion();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
@@ -4017,10 +4034,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
- public class ImsManager {
- field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
- }
-
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @Deprecated @NonNull @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -4282,82 +4295,12 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
- field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
- field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
- field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
- field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
- field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
- field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
- field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
- field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
- field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
- field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
- field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
- field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
- field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
- field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
- field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
- field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
- field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
- field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
- field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
- field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
- field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
- field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
- field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
- field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
- field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
- field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
- field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
- field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10
- field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
- field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
- field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
- field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
- field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
- field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
- field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
- field public static final int KEY_RTT_ENABLED = 66; // 0x42
- field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
- field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
- field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
- field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
- field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
- field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
- field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
- field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
- field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
- field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
- field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
- field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
- field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
- field public static final int KEY_SMS_FORMAT = 13; // 0xd
- field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
- field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
- field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
- field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
- field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
- field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
- field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
- field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
- field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
- field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
- field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
- field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
- field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
- field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
- field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
- field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
- field public static final int SMS_FORMAT_3GPP = 1; // 0x1
- field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
- field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
- field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
}
public static class ProvisioningManager.Callback {
@@ -4366,56 +4309,6 @@
method public void onProvisioningStringChanged(int, @NonNull String);
}
- public final class RcsContactUceCapability implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
- method @NonNull public android.net.Uri getContactUri();
- method @Nullable public android.net.Uri getServiceUri(long);
- method public boolean isCapable(long);
- method public boolean isCapable(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
- field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
- field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
- field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
- field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
- field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
- field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
- field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
- field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
- field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
- field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
- field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
- field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
- field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
- field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
- field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
- field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
- field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
- field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
- field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
- field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
- field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
- field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
- field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
- field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
- field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
- field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
- field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
- field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
- field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
- field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
- }
-
- public static class RcsContactUceCapability.Builder {
- ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
- method @NonNull public android.telephony.ims.RcsContactUceCapability build();
- }
-
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
@@ -4662,24 +4555,12 @@
method public int transact(android.os.Bundle);
method public int updateCallBarring(int, int, String[]);
method public int updateCallBarringForServiceClass(int, int, String[], int);
- method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
method public int updateCallForward(int, int, String, int, int);
method public int updateCallWaiting(boolean, int);
method public int updateClip(boolean);
method public int updateClir(int);
method public int updateColp(boolean);
method public int updateColr(int);
- field public static final int CALL_BARRING_ALL = 7; // 0x7
- field public static final int CALL_BARRING_ALL_INCOMING = 1; // 0x1
- field public static final int CALL_BARRING_ALL_OUTGOING = 2; // 0x2
- field public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6; // 0x6
- field public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9; // 0x9
- field public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8; // 0x8
- field public static final int CALL_BARRING_OUTGOING_INTL = 3; // 0x3
- field public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4; // 0x4
- field public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10; // 0xa
- field public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5; // 0x5
- field public static final int INVALID_RESULT = -1; // 0xffffffff
}
}
@@ -4951,8 +4832,8 @@
}
public class SurfaceControlViewHost {
- method public void addView(@NonNull android.view.View, android.view.WindowManager.LayoutParams);
method public void relayout(android.view.WindowManager.LayoutParams);
+ method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams);
}
@UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
@@ -4968,7 +4849,7 @@
method public void resetRtlProperties();
method public boolean restoreFocusInCluster(int);
method public boolean restoreFocusNotInCluster();
- method public void setAutofilled(boolean);
+ method public void setAutofilled(boolean, boolean);
method public final void setFocusedInCluster();
method public void setIsRootNamespace(boolean);
method public final void setShowingLayoutBounds(boolean);
@@ -5082,6 +4963,7 @@
method public long getEventTime();
method @Nullable public android.view.autofill.AutofillId getId();
method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
+ method @Nullable public android.graphics.Insets getInsets();
method @Nullable public CharSequence getText();
method public int getType();
method @Nullable public android.view.contentcapture.ViewNode getViewNode();
@@ -5092,6 +4974,7 @@
field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
+ field public static final int TYPE_VIEW_INSETS_CHANGED = 9; // 0x9
field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 94db346..caf8fdb 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -421,9 +421,9 @@
GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
-GetterSetterNames: android.location.GnssMeasurement#setReceiverInterSignalBiasNanos(double):
+GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasNanos(double):
-GetterSetterNames: android.location.GnssMeasurement#setReceiverInterSignalBiasUncertaintyNanos(double):
+GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasUncertaintyNanos(double):
GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasNanos(double):
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index f84e4b5..8f5e49d 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -113,7 +113,7 @@
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
- std::unique_ptr<std::string>* _aidl_return) {
+ aidl::nullable<std::string>* _aidl_return) {
assert(_aidl_return);
SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path;
_aidl_return->reset(nullptr);
@@ -155,7 +155,7 @@
return error("failed to write to idmap path " + idmap_path);
}
- *_aidl_return = std::make_unique<std::string>(idmap_path);
+ *_aidl_return = aidl::make_nullable<std::string>(idmap_path);
return ok();
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 94d2af4..b6f5136 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -19,9 +19,7 @@
#include <android-base/unique_fd.h>
#include <binder/BinderService.h>
-
-#include <memory>
-#include <string>
+#include <binder/Nullable.h>
#include "android/os/BnIdmap2.h"
@@ -46,7 +44,7 @@
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
- std::unique_ptr<std::string>* _aidl_return) override;
+ aidl::nullable<std::string>* _aidl_return) override;
};
} // namespace android::os
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 32a5243..1cbe150 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -398,11 +398,13 @@
256 [(module) = "framework"];
DisplayJankReported display_jank_reported = 257;
AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
+ SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
+ RankingSelected ranking_selected = 260 [(module) = "framework"];
SdkExtensionStatus sdk_extension_status = 354;
}
// Pulled events will start at field 10000.
- // Next: 10076
+ // Next: 10080
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -485,7 +487,11 @@
PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences =
10073 [(module) = "framework"];
GnssStats gnss_stats = 10074 [(module) = "framework"];
- AppFeaturesOps app_features_ops = 10075 [(module) = "framework"];
+ AttributedAppOps attributed_app_ops = 10075 [(module) = "framework"];
+ VoiceCallSession voice_call_session = 10076 [(module) = "telephony"];
+ VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"];
+ SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
+ SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3328,16 +3334,12 @@
optional int32 uid = 1 [(is_uid) = true];
// The operation's name.
- // To the extent possible, preserve the mapping from AppOpsManager.OP_ constants.
- // Only these named ops are actually logged.
- enum AppOpName {
- OP_NONE = -1; // Also represents UNKNOWN.
- OP_COARSE_LOCATION = 0;
- OP_FINE_LOCATION = 1;
- OP_CAMERA = 26;
- OP_RECORD_AUDIO = 27;
- }
- optional AppOpName app_op_name = 2 [default = OP_NONE];
+ // Only following four ops are logged
+ // COARSE_LOCATION = 0
+ // FINE_LOCATION = 1
+ // CAMERA = 26
+ // RECORD_AUDIO = 27
+ optional android.app.AppOpEnum app_op_name = 2 [default = APP_OP_NONE];
// The uid's permission mode for accessing the AppOp during this fgs session.
enum Mode {
@@ -4477,8 +4479,7 @@
* after an OTA.
*
* Logged from:
- * - system/core/fs_mgr/libsnapshot/snapshot.cpp
- * - system/core/fs_mgr/libsnapshot/snapshotctl.cpp
+ * - system/update_engine/cleanup_previous_update_action.cc
*/
message SnapshotMergeReported {
// Keep in sync with
@@ -7568,8 +7569,8 @@
// Name of the package performing the op
optional string package_name = 2;
- // operation id; maps to the OP_* constants in AppOpsManager.java
- optional int32 op_id = 3;
+ // operation id
+ optional android.app.AppOpEnum op_id = 3 [default = APP_OP_NONE];
// The number of times the op was granted while the app was in the
// foreground (only for trusted requests)
@@ -7600,21 +7601,22 @@
}
/**
- * Historical app ops data per package and features.
+ * Historical app ops data per package and attribution tag.
*/
-message AppFeaturesOps {
+message AttributedAppOps {
// Uid of the package requesting the op
optional int32 uid = 1 [(is_uid) = true];
// Name of the package performing the op
optional string package_name = 2;
- // feature id; provided by developer when accessing related API, limited at 50 chars by API.
- // Features must be provided through manifest using <feature> tag available in R and above.
- optional string feature_id = 3;
+ // tag; provided by developer when accessing related API, limited at 50 chars by API.
+ // Attributions must be provided through manifest using <attribution> tag available in R and
+ // above.
+ optional string tag = 3;
- // operation id; maps to the OPSTR_* constants in AppOpsManager.java
- optional string op = 4;
+ // operation id
+ optional android.app.AppOpEnum op = 4 [default = APP_OP_NONE];
// The number of times the op was granted while the app was in the
// foreground (only for trusted requests)
@@ -8473,9 +8475,11 @@
// operation string id per OPSTR_ constants in AppOpsManager.java
optional string op = 3;
- // feature id; provided by developer when accessing related API, limited at 50 chars by API.
- // Features must be provided through manifest using <feature> tag available in R and above.
- optional string feature_id = 4;
+ // attribution_tag; provided by developer when accessing related API, limited at 50 chars by
+ // API.
+ // Attributions must be provided through manifest using <attribution> tag available in R and
+ // above.
+ optional string attribution_tag = 4;
// message related to app op access, limited to 600 chars by API
optional string message = 5;
@@ -8484,6 +8488,7 @@
DEFAULT = 0;
UNIFORM = 1;
RARELY_USED = 2;
+ BOOT_TIME_SAMPLING = 3;
}
// sampling strategy used to collect this message
@@ -8672,6 +8677,154 @@
}
/**
+ * Pulls information for a single voice call.
+ *
+ * Each pull creates multiple atoms, one for each call. The sequence is randomized when pulled.
+ *
+ * Pulled from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/PersistPullers.java
+ */
+message VoiceCallSession {
+ // Bearer (IMS or CS) when the call started.
+ optional android.telephony.CallBearerEnum bearer_at_start = 1;
+
+ // Bearer (IMS or CS) when the call ended.
+ // The bearer may change during the call, e.g. due to SRVCC.
+ optional android.telephony.CallBearerEnum bearer_at_end = 2;
+
+ // Direction of the call (incoming or outgoing).
+ optional android.telephony.CallDirectionEnum direction = 3;
+
+ // Time spent setting up the call.
+ optional android.telephony.CallSetupDurationEnum setup_duration = 4;
+
+ // Whether the call ended before the setup was completed.
+ optional bool setup_failed = 5;
+
+ // IMS reason code or CS disconnect cause.
+ // For IMS, see: frameworks/base/telephony/java/android/telephony/ims/ImsReasonInfo.java
+ // For CS, see: frameworks/base/telephony/java/android/telephony/DisconnectCause.java
+ optional int32 disconnect_reason_code = 6;
+
+ // IMS extra code or CS precise disconnect cause.
+ // For IMS, this code is vendor-specific
+ // For CS, see: frameworks/base/telephony/java/android/telephony/PreciseDisconnectCause.java
+ optional int32 disconnect_extra_code = 7;
+
+ // IMS extra message or CS vendor cause.
+ optional string disconnect_extra_message = 8;
+
+ // Radio access technology (RAT) used when call started.
+ optional android.telephony.NetworkTypeEnum rat_at_start = 9;
+
+ // Radio access technology (RAT) used when call terminated.
+ optional android.telephony.NetworkTypeEnum rat_at_end = 10;
+
+ // Number of times RAT changed during the call.
+ optional int64 rat_switch_count = 11;
+
+ // A bitmask of all codecs used during the call.
+ // See: frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+ optional int64 codec_bitmask = 12;
+
+ // Number of other calls going on during call setup, for the same SIM slot.
+ optional int32 concurrent_call_count_at_start = 13;
+
+ // Number of other calls going on during call termination, for the same SIM slot.
+ optional int32 concurrent_call_count_at_end = 14;
+
+ // Index of the SIM is used, 0 for single-SIM devices.
+ optional int32 sim_slot_index = 15;
+
+ // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+ optional bool is_multi_sim = 16;
+
+ // Whether the call was made with an eSIM profile.
+ optional bool is_esim = 17;
+
+ // Carrier ID of the SIM card.
+ // See https://source.android.com/devices/tech/config/carrierid.
+ optional int32 carrier_id = 18;
+
+ // Whether an SRVCC has been completed successfully.
+ // SRVCC (CS fallback) should be recorded in the IMS call since there will be no more SRVCC
+ // events once the call is switched to CS.
+ optional bool srvcc_completed = 19;
+
+ // Number of SRVCC failures.
+ optional int64 srvcc_failure_count = 20;
+
+ // Number of SRVCC cancellations.
+ optional int64 srvcc_cancellation_count = 21;
+
+ // Whether the Real-Time Text (RTT) was ever used in the call.
+ optional bool rtt_enabled = 22;
+
+ // Whether this was an emergency call.
+ optional bool is_emergency = 23;
+
+ // Whether the call was performed while roaming.
+ optional bool is_roaming = 24;
+}
+
+/**
+ * Pulls voice call radio access technology (RAT) usage.
+ *
+ * Each pull creates multiple atoms, one for each carrier/RAT, the order of which is irrelevant to
+ * time. The atom will be skipped if not enough data is available.
+ *
+ * Pulled from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/PersistPullers.java
+ */
+message VoiceCallRatUsage {
+ // Carrier ID (https://source.android.com/devices/tech/config/carrierid).
+ optional int32 carrier_id = 1;
+
+ // Radio access technology.
+ optional android.telephony.NetworkTypeEnum rat = 2;
+
+ // Total duration that voice calls spent on this carrier and RAT.
+ optional int64 total_duration_seconds = 3;
+
+ // Total number of calls using this carrier and RAT.
+ // A call is counted once even if it used the RAT multiple times.
+ optional int64 call_count = 4;
+}
+
+/**
+ * Pulls the number of active SIM slots and SIMs/eSIM profiles.
+ *
+ * Pulled from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/NonPersistPullers.java
+ */
+message SimSlotState {
+ // Number of active SIM slots (both physical and eSIM profiles) in the device.
+ optional int32 active_slot_count = 1;
+
+ // Number of SIM cards (both physical and active eSIM profiles).
+ // This number is always equal to or less than the number of active SIM slots.
+ optional int32 sim_count = 2;
+
+ // Number of active eSIM profiles.
+ // This number is always equal to or less than the number of SIMs.
+ optional int32 esim_count = 3;
+}
+
+/**
+ * Pulls supported cellular radio access technologies.
+ *
+ * This atom reports the capabilities of the device, rather than the network it has access to.
+ *
+ * Pulled from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/NonPersistPullers.java
+ */
+message SupportedRadioAccessFamily {
+ // A bitmask of supported radio technologies.
+ // See android.telephony.TelephonyManager.NetworkTypeBitMask.
+ optional int64 network_type_bitmask = 1;
+}
+
+/**
* Logs gnss stats from location service provider
*
* Pulled from:
@@ -8763,3 +8916,64 @@
// UsageStatsManager.java.
optional int32 sub_reason = 5;
}
+
+/**
+* Reports a started sharesheet transaction.
+*
+* Logged from:
+* frameworks/base/core/java/com/android/internal/app/ChooserActivity.java
+*/
+message SharesheetStarted {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The calling app's package name.
+ optional string package_name = 2;
+ // An identifier to tie together multiple logs relating to the same share event
+ optional int32 instance_id = 3;
+ // The mime type of the share
+ optional string mime_type = 4;
+ // The number of direct targets the calling app is providing that will be shown.
+ optional int32 num_app_provided_direct_targets = 5;
+ // The number of app targets the calling app is providing that will be shown.
+ optional int32 num_app_provided_app_targets = 6;
+ // True if the share originates from the workprofile
+ optional bool is_workprofile = 7;
+
+ enum SharesheetPreviewType { // Constants from ChooserActivity.java
+ CONTENT_PREVIEW_IMAGE = 1; // The preview shown in the sharesheet is an image.
+ CONTENT_PREVIEW_FILE = 2; // The preview shown in the sharesheet is a file.
+ CONTENT_PREVIEW_TEXT = 3; // The preview shown in the sharesheet is text.
+ }
+ // How the sharesheet preview is presented.
+ optional SharesheetPreviewType previewType = 8;
+
+ enum ResolverActivityIntent { // Intents handled by ResolverActivity.java
+ INTENT_DEFAULT = 0;
+ INTENT_ACTION_VIEW = 1;
+ INTENT_ACTION_EDIT = 2;
+ INTENT_ACTION_SEND = 3;
+ INTENT_ACTION_SENDTO = 4;
+ INTENT_ACTION_SEND_MULTIPLE = 5;
+ INTENT_ACTION_IMAGE_CAPTURE = 6;
+ INTENT_ACTION_MAIN = 7;
+ }
+ // The intent being processed (only SEND and SEND_MULTIPLE are system sharesheet)
+ optional ResolverActivityIntent intentType = 9;
+}
+
+/**
+ * Reports a ranking selection event.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/app/ChooserActivity.java (sharesheet)
+ */
+message RankingSelected {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The relevant app's package name (can be source or picked package).
+ optional string package_name = 2;
+ // An identifier to tie together multiple logs relating to the same share event.
+ optional int32 instance_id = 3;
+ // Which of the ranked targets got picked, default starting position 0.
+ optional int32 position_picked = 4;
+}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 258f84d..b515d0a 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -350,17 +350,19 @@
return false;
}
-void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t value = readNextValue<int32_t>();
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int64_t value = readNextValue<int64_t>();
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numBytes = readNextValue<int32_t>();
if ((uint32_t)numBytes > mRemainingLen) {
mValid = false;
@@ -371,20 +373,23 @@
mBuf += numBytes;
mRemainingLen -= numBytes;
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
float value = readNextValue<float>();
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
// cast to int32_t because FieldValue does not support bools
int32_t value = (int32_t)readNextValue<uint8_t>();
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numBytes = readNextValue<int32_t>();
if ((uint32_t)numBytes > mRemainingLen) {
mValid = false;
@@ -395,9 +400,10 @@
mBuf += numBytes;
mRemainingLen -= numBytes;
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numPairs = readNextValue<uint8_t>();
for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) {
@@ -405,56 +411,79 @@
// parse key
pos[2] = 1;
- parseInt32(pos, 2, last);
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
// parse value
last[2] = true;
- uint8_t typeId = getTypeId(readNextValue<uint8_t>());
- switch (typeId) {
+
+ uint8_t typeInfo = readNextValue<uint8_t>();
+ switch (getTypeId(typeInfo)) {
case INT32_TYPE:
pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto
- parseInt32(pos, 2, last);
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case INT64_TYPE:
pos[2] = 3;
- parseInt64(pos, 2, last);
+ parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case STRING_TYPE:
pos[2] = 4;
- parseString(pos, 2, last);
+ parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case FLOAT_TYPE:
pos[2] = 5;
- parseFloat(pos, 2, last);
+ parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
default:
mValid = false;
}
}
+ parseAnnotations(numAnnotations);
+
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
}
-void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
+ uint8_t numAnnotations) {
int32_t numNodes = readNextValue<uint8_t>();
for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
last[1] = (pos[1] == numNodes);
// parse uid
pos[2] = 1;
- parseInt32(pos, 2, last);
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
// parse tag
pos[2] = 2;
last[2] = true;
- parseString(pos, 2, last);
+ parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
}
+ parseAnnotations(numAnnotations);
+
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
}
+// TODO(b/151109630): store annotation information within LogEvent
+void LogEvent::parseAnnotations(uint8_t numAnnotations) {
+ for (uint8_t i = 0; i < numAnnotations; i++) {
+ /*uint8_t annotationId = */ readNextValue<uint8_t>();
+ uint8_t annotationType = readNextValue<uint8_t>();
+ switch (annotationType) {
+ case BOOL_TYPE:
+ /*bool annotationValue = */ readNextValue<uint8_t>();
+ break;
+ case INT32_TYPE:
+ /*int32_t annotationValue =*/ readNextValue<int32_t>();
+ break;
+ default:
+ mValid = false;
+ }
+ }
+}
// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
// stats_event.c
@@ -475,6 +504,7 @@
typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
mElapsedTimestampNs = readNextValue<int64_t>();
+ parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
numElements--;
typeInfo = readNextValue<uint8_t>();
@@ -484,37 +514,36 @@
for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) {
+ last[0] = (pos[0] == numElements);
+
typeInfo = readNextValue<uint8_t>();
uint8_t typeId = getTypeId(typeInfo);
- last[0] = (pos[0] == numElements);
-
// TODO(b/144373276): handle errors passed to the socket
- // TODO(b/144373257): parse annotations
switch(typeId) {
case BOOL_TYPE:
- parseBool(pos, 0, last);
+ parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case INT32_TYPE:
- parseInt32(pos, 0, last);
+ parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case INT64_TYPE:
- parseInt64(pos, 0, last);
+ parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case FLOAT_TYPE:
- parseFloat(pos, 0, last);
+ parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case BYTE_ARRAY_TYPE:
- parseByteArray(pos, 0, last);
+ parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case STRING_TYPE:
- parseString(pos, 0, last);
+ parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case KEY_VALUE_PAIRS_TYPE:
- parseKeyValuePairs(pos, 0, last);
+ parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case ATTRIBUTION_CHAIN_TYPE:
- parseAttributionChain(pos, 0, last);
+ parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
default:
mValid = false;
@@ -531,7 +560,7 @@
}
uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) {
- return (typeInfo >> 4) & 0x0F;
+ return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes
}
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index b68eeb8..6537f13 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -232,14 +232,15 @@
*/
LogEvent(const LogEvent&);
- void parseInt32(int32_t* pos, int32_t depth, bool* last);
- void parseInt64(int32_t* pos, int32_t depth, bool* last);
- void parseString(int32_t* pos, int32_t depth, bool* last);
- void parseFloat(int32_t* pos, int32_t depth, bool* last);
- void parseBool(int32_t* pos, int32_t depth, bool* last);
- void parseByteArray(int32_t* pos, int32_t depth, bool* last);
- void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last);
- void parseAttributionChain(int32_t* pos, int32_t depth, bool* last);
+ void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseAnnotations(uint8_t numAnnotations);
/**
* The below three variables are only valid during the execution of
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index e48f378..4848918 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -79,153 +79,695 @@
}
}
-// TODO(b/149590301): Update these tests to use new socket schema.
-//class ValueMetricProducerTestHelper {
-//
-// public:
-// static shared_ptr<LogEvent> createEvent(int64_t eventTimeNs, int64_t value) {
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventTimeNs);
-// event->write(tagId);
-// event->write(value);
-// event->write(value);
-// event->init();
-// return event;
-// }
-//
-// static sp<ValueMetricProducer> createValueProducerNoConditions(
-// sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-// kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-// logEventMatcherIndex, eventMatcherWizard, tagId,
-// bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-// return valueProducer;
-// }
-//
-// static sp<ValueMetricProducer> createValueProducerWithCondition(
-// sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// sp<ValueMetricProducer> valueProducer =
-// new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-// valueProducer->mCondition = ConditionState::kFalse;
-// return valueProducer;
-// }
-//
-// static sp<ValueMetricProducer> createValueProducerWithNoInitialCondition(
-// sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-// kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-// bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-// return valueProducer;
-// }
-//
-// static sp<ValueMetricProducer> createValueProducerWithState(
-// sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
-// vector<int32_t> slicedStateAtoms,
-// unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-// sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-// kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
-// {}, slicedStateAtoms, stateGroupMap);
-// return valueProducer;
-// }
-//
-// static ValueMetric createMetric() {
-// ValueMetric metric;
-// metric.set_id(metricId);
-// metric.set_bucket(ONE_MINUTE);
-// metric.mutable_value_field()->set_field(tagId);
-// metric.mutable_value_field()->add_child()->set_field(2);
-// metric.set_max_pull_delay_sec(INT_MAX);
-// return metric;
-// }
-//
-// static ValueMetric createMetricWithCondition() {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_condition(StringToId("SCREEN_ON"));
-// return metric;
-// }
-//
-// static ValueMetric createMetricWithState(string state) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.add_slice_by_state(StringToId(state));
-// return metric;
-// }
-//};
-//
-///*
-// * Tests that the first bucket works correctly
-// */
-//TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// int64_t startTimeBase = 11;
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// // statsd started long ago.
-// // The metric starts in the middle of the bucket
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-// logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
-// 22, pullerManager);
-//
-// EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
-// EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
-// EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
-// valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
-// EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
-// valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
-//}
-//
-///*
-// * Tests that the first bucket works correctly
-// */
-//TEST(ValueMetricProducerTest, TestFirstBucket) {
+class ValueMetricProducerTestHelper {
+public:
+ static sp<ValueMetricProducer> createValueProducerNoConditions(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ return valueProducer;
+ }
+
+ static sp<ValueMetricProducer> createValueProducerWithCondition(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ valueProducer->mCondition = ConditionState::kFalse;
+ return valueProducer;
+ }
+
+ static sp<ValueMetricProducer> createValueProducerWithNoInitialCondition(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ return valueProducer;
+ }
+
+ static sp<ValueMetricProducer> createValueProducerWithState(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
+ vector<int32_t> slicedStateAtoms,
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
+ {}, slicedStateAtoms, stateGroupMap);
+ return valueProducer;
+ }
+
+ static ValueMetric createMetric() {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_max_pull_delay_sec(INT_MAX);
+ return metric;
+ }
+
+ static ValueMetric createMetricWithCondition() {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_condition(StringToId("SCREEN_ON"));
+ return metric;
+ }
+
+ static ValueMetric createMetricWithState(string state) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.add_slice_by_state(StringToId(state));
+ return metric;
+ }
+};
+
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ int64_t startTimeBase = 11;
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ // statsd started long ago.
+ // The metric starts in the middle of the bucket
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
+ 22, pullerManager);
+
+ EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
+ EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
+ EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
+ valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
+ EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
+ valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
+}
+
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestFirstBucket) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ // statsd started long ago.
+ // The metric starts in the middle of the bucket
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, -1, 5,
+ 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+
+ EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
+}
+
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
+
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(8, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(23, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(12, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+ EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(13, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+ EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
+ EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
+}
+
+TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initialize bucket.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
+ return true;
+ }))
+ // Partial bucket.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ // First bucket ends.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2));
+ valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
+
+ // Partial buckets created in 2nd bucket.
+ valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+
+ // One full bucket and one partial bucket.
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second;
+ EXPECT_EQ(2UL, buckets.size());
+ // Full bucket (2 - 1)
+ EXPECT_EQ(1, buckets[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
+ // Full bucket (5 - 3)
+ EXPECT_EQ(3, buckets[1].values[0].long_value);
+ // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
+ EXPECT_EQ(2, buckets[1].mConditionTrueNs);
+}
+
+/*
+ * Tests pulled atoms with filtering
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ auto keyValue = atomMatcher.add_field_value_matcher();
+ keyValue->set_field(1);
+ keyValue->set_eq_int(3);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11));
+
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(8, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ // No new data seen, so data has been cleared.
+ EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(8, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ // the base was reset
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
+}
+
+/*
+ * Tests pulled atoms with no conditions and take absolute value after reset
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_use_absolute_value_on_reset(true);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
+
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(10, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(26, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
+ EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+ EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
+}
+
+/*
+ * Tests pulled atoms with no conditions and take zero value after reset
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
+
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(10, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(26, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+}
+
+/*
+ * Test pulled event with non sliced condition.
+ */
+TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ // startUpdated:false sum:0 start:100
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 110));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(110, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(10, curInterval.value.long_value);
+
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(20, curInterval.value.long_value);
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+
+ valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1});
+}
+
+TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+
+ valueProducer.notifyAppUpgrade(bucketStartTimeNs + 150, "ANY.APP", 1, 1);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
+
+ // Next value should create a new bucket.
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
+}
+
+TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Return(true))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 149, 120));
+ return true;
+ }));
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
+
+ valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+
+ valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150});
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150));
+ valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(20L,
+ valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30},
+ {150, bucketSizeNs - 150});
+}
+
+TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_split_bucket_for_app_upgrade(false);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
+
+ valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+
+ valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+}
+
+TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 120));
+ return true;
+ }));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
+
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
+ EXPECT_FALSE(valueProducer->mCondition);
+
+ valueProducer->notifyAppUpgrade(bucket2StartTimeNs - 50, "ANY.APP", 1, 1);
+ // Expect one full buckets already done and starting a partial bucket.
+ EXPECT_EQ(bucket2StartTimeNs - 50, valueProducer->mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs,
+ valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20},
+ {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)});
+ EXPECT_FALSE(valueProducer->mCondition);
+}
+
+// TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -237,676 +779,10 @@
// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
-// // statsd started long ago.
-// // The metric starts in the middle of the bucket
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-// logEventMatcherIndex, eventMatcherWizard, -1, 5,
-// 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
-//
-// EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
-// EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
-// EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
-//}
-//
-///*
-// * Tests pulled atoms with no conditions
-// */
-//TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(3);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(11);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(8, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(tagId);
-// event->write(23);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(23, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(12, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-// EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
-// event->write(tagId);
-// event->write(36);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(36, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(13, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-// EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
-// EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
-//}
-//
-//TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Initialize bucket.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
-// event->write(tagId);
-// event->write(1);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// // Partial bucket.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-// event->write(tagId);
-// event->write(5);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// // First bucket ends.
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-// event->write(tagId);
-// event->write(2);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
-//
-// // Partial buckets created in 2nd bucket.
-// valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
-//
-// // One full bucket and one partial bucket.
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second;
-// EXPECT_EQ(2UL, buckets.size());
-// // Full bucket (2 - 1)
-// EXPECT_EQ(1, buckets[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
-// // Full bucket (5 - 3)
-// EXPECT_EQ(3, buckets[1].values[0].long_value);
-// // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
-// EXPECT_EQ(2, buckets[1].mConditionTrueNs);
-//}
-//
-///*
-// * Tests pulled atoms with filtering
-// */
-//TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// auto keyValue = atomMatcher.add_field_value_matcher();
-// keyValue->set_field(1);
-// keyValue->set_eq_int(3);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(3);
-// event->write(3);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-// kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(3);
-// event->write(11);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(8, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(4);
-// event->write(23);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// // No new data seen, so data has been cleared.
-// EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(8, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
-// event->write(3);
-// event->write(36);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// // the base was reset
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(36, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-//}
-//
-///*
-// * Tests pulled atoms with no conditions and take absolute value after reset
-// */
-//TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_use_absolute_value_on_reset(true);
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(11);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(tagId);
-// event->write(10);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(10, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(10, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
-// event->write(tagId);
-// event->write(36);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(36, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(26, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
-// EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-// EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
-//}
-//
-///*
-// * Tests pulled atoms with no conditions and take zero value after reset
-// */
-//TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(11);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(tagId);
-// event->write(10);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(10, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
-// event->write(tagId);
-// event->write(36);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(36, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(26, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//}
-//
-///*
-// * Test pulled event with non sliced condition.
-// */
-//TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
-// event->write(tagId);
-// event->write(100);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(130);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(tagId);
-// event->write(180);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// // startUpdated:false sum:0 start:100
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(100, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(1);
-// event->write(110);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(110, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(10, curInterval.value.long_value);
-//
-// valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(20, curInterval.value.long_value);
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-//
-// valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1});
-//}
-//
-//TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-//
-// valueProducer.notifyAppUpgrade(bucketStartTimeNs + 150, "ANY.APP", 1, 1);
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-// EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
-//
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
-// event2->write(1);
-// event2->write(10);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-// EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
-//
-// // Next value should create a new bucket.
-// shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
-// event3->write(1);
-// event3->write(10);
-// event3->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
-// EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
-//}
-//
-//TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// .WillOnce(Return(true))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 149);
-// event->write(tagId);
-// event->write(120);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs,
+// eventMatcherWizard, -1, bucketStartTimeNs,
// bucketStartTimeNs, pullerManager);
//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(100);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-//
-// valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-// EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
-// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150});
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(tagId);
-// event->write(150);
-// event->init();
-// allData.push_back(event);
-// valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-// EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
-// EXPECT_EQ(20L,
-// valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
-// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30},
-// {150, bucketSizeNs - 150});
-//}
-//
-//TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_split_bucket_for_app_upgrade(false);
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(100);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-//
-// valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
-// EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-// EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
-//}
-//
-//TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
-// event->write(tagId);
-// event->write(100);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
-// event->write(tagId);
-// event->write(120);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
-//
-// valueProducer->onConditionChanged(false, bucket2StartTimeNs-100);
-// EXPECT_FALSE(valueProducer->mCondition);
-//
-// valueProducer->notifyAppUpgrade(bucket2StartTimeNs-50, "ANY.APP", 1, 1);
-// // Expect one full buckets already done and starting a partial bucket.
-// EXPECT_EQ(bucket2StartTimeNs-50, valueProducer->mCurrentBucketStartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-// EXPECT_EQ(bucketStartTimeNs,
-// valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20},
-// {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)});
-// EXPECT_FALSE(valueProducer->mCondition);
-//}
-//
-//TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
-//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
// event1->write(10);
@@ -918,10 +794,10 @@
// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
// // has one slice
// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(10, curInterval.value.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
+// ValueMetricProducer::Interval curInterval =
+// valueProducer.mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(10,
+// curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue);
//
// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
//
@@ -934,7 +810,7 @@
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
+// TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -947,8 +823,8 @@
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
// valueProducer.mCondition = ConditionState::kFalse;
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -1000,7 +876,7 @@
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20});
//}
//
-//TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+// TEST(ValueMetricProducerTest, TestAnomalyDetection) {
// sp<AlarmMonitor> alarmMonitor;
// Alert alert;
// alert.set_id(101);
@@ -1053,7 +929,8 @@
// event5->write(150); // value of interest
// event5->init();
// shared_ptr<LogEvent> event6
-// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
+// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 *
+// NS_PER_SEC);
// event6->write(25);
// event6->write(160); // value of interest
// event6->init();
@@ -1086,7 +963,7 @@
//}
//
//// Test value metric no condition, the pull on bucket boundary come in time and too late
-//TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
@@ -1107,7 +984,8 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0];
//
// // startUpdated:true sum:0 start:11
// EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1158,7 +1036,7 @@
// * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
// * was delivered late.
// */
-//TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -1184,7 +1062,8 @@
// return true;
// }));
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
//
@@ -1192,8 +1071,8 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
@@ -1219,10 +1098,11 @@
//}
//
///*
-// * Test pulled event with non sliced condition. The pull on boundary come late, after the condition
+// * Test pulled event with non sliced condition. The pull on boundary come late, after the
+// condition
// * change to false, and then true again. This is due to alarm delivered late.
// */
-//TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -1250,16 +1130,15 @@
// // condition becomes true again
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 25);
-// event->write(tagId);
-// event->write(130);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 25); event->write(tagId); event->write(130); event->init();
// data->push_back(event);
// return true;
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
//
@@ -1267,7 +1146,8 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0];
// // startUpdated:false sum:0 start:100
// EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1313,7 +1193,7 @@
// {bucketSizeNs - 8, bucketSizeNs - 24});
//}
//
-//TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::MIN);
//
@@ -1327,8 +1207,8 @@
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1357,7 +1237,7 @@
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::MAX);
//
@@ -1371,8 +1251,8 @@
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1403,7 +1283,7 @@
// /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
//}
//
-//TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::AVG);
//
@@ -1417,8 +1297,8 @@
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1449,11 +1329,12 @@
// EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
// EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
//
-// EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value -
+// EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value
+// -
// 12.5) < epsilon);
//}
//
-//TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::SUM);
//
@@ -1467,8 +1348,8 @@
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1497,7 +1378,7 @@
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
+// TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::MIN);
// metric.set_use_diff(true);
@@ -1512,8 +1393,8 @@
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1572,7 +1453,7 @@
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
+// TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_value_field()->add_child()->set_field(3);
// metric.set_aggregation_type(ValueMetric::MIN);
@@ -1588,8 +1469,8 @@
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1687,7 +1568,7 @@
///*
// * Tests zero default base.
// */
-//TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
+// TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -1774,7 +1655,7 @@
///*
// * Tests using zero default base with failed pull.
// */
-//TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
+// TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -1885,8 +1766,9 @@
// it2 = std::next(valueProducer->mCurrentSlicedBucket.begin());
// interval1 = it->second[0];
// interval2 = it2->second[0];
-// baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
-// baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+// baseInfo1 =
+// valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; baseInfo2
+// = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
//
// EXPECT_EQ(true, baseInfo1.hasBase);
// EXPECT_EQ(5, baseInfo1.base.long_value);
@@ -1905,7 +1787,7 @@
///*
// * Tests trim unused dimension key if no new data is seen in an entire bucket.
// */
-//TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
+// TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -2023,7 +1905,7 @@
// EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2040,15 +1922,16 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// // has one slice
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo& curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
//
@@ -2060,7 +1943,7 @@
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2077,7 +1960,8 @@
// .WillOnce(Return(false));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
//
@@ -2085,8 +1969,8 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo& curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
@@ -2100,7 +1984,7 @@
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2125,7 +2009,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Don't directly set mCondition; the real code never does that. Go through regular code path
// // to avoid unexpected behaviors.
@@ -2138,13 +2023,13 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_condition(StringToId("SCREEN_ON"));
// metric.set_max_pull_delay_sec(0);
@@ -2162,7 +2047,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kFalse;
//
@@ -2171,7 +2057,7 @@
// EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// UidMap uidMap;
@@ -2196,7 +2082,7 @@
// EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
//}
//
-//TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
+// TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2212,7 +2098,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kFalse;
// valueProducer->mHasGlobalBase = false;
@@ -2222,8 +2109,8 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2232,7 +2119,7 @@
///*
// * Tests that a bucket is marked invalid when a condition change pull fails.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2251,7 +2138,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kTrue;
//
@@ -2286,8 +2174,8 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(140, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2317,7 +2205,7 @@
///*
// * Tests that a bucket is marked invalid when the guardrail is hit.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -2339,7 +2227,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
// valueProducer->mCondition = ConditionState::kFalse;
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 2);
@@ -2385,7 +2274,7 @@
///*
// * Tests that a bucket is marked invalid when the bucket's initial pull fails.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2412,7 +2301,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kTrue;
//
@@ -2445,8 +2335,8 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(140, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2477,7 +2367,7 @@
// * Tests that a bucket is marked invalid when the bucket's final pull fails
// * (i.e. failed pull on bucket boundary).
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2504,7 +2394,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kTrue;
//
@@ -2537,8 +2428,8 @@
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//
@@ -2564,7 +2455,7 @@
// EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
//}
//
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
+// TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// EXPECT_CALL(*pullerManager, Pull(tagId, _))
@@ -2604,999 +2495,903 @@
// EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
//}
-//
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // First onConditionChanged
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(3);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval& curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-//
-// // Empty pull.
-// valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
-//}
-//
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // First onConditionChanged
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(1);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(2);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(5);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
-// valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval& curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-//
-// // End of bucket
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// // Data is empty, base should be reset.
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-// EXPECT_EQ(5, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-//
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1});
-//}
-//
-//TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.mutable_dimensions_in_what()->set_field(tagId);
-// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
-// metric.set_condition(StringToId("SCREEN_ON"));
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // First onConditionChanged
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(1);
-// event->write(1);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//
-// // End of bucket
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(2);
-// event->write(2);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// // Key 1 should be reset since in not present in the most pull.
-// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
-// auto iterator = valueProducer->mCurrentSlicedBucket.begin();
-// auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
-// EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
-// EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
-// EXPECT_EQ(false, iterator->second[0].hasValue);
-// iterator++;
-// baseInfoIter++;
-// EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
-// EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
-// EXPECT_EQ(false, iterator->second[0].hasValue);
-//
-// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-//}
-//
-//TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Initialization.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
-// return true;
-// }))
-// // notifyAppUpgrade.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(
-// bucketStartTimeNs + bucketSizeNs / 2, 10));
-// return true;
-// }));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-// ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
-//
-// valueProducer->notifyAppUpgrade(bucketStartTimeNs + bucketSizeNs / 2, "com.foo", 10000, 1);
-// ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size());
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 1, 4));
-// valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1);
-// ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
-//}
-//
-//TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Second onConditionChanged.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(
-// ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5));
-// return true;
-// }))
-// // Third onConditionChanged.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(
-// ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7));
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-// valueProducer->mCondition = ConditionState::kUnknown;
-//
-// valueProducer->onConditionChanged(false, bucketStartTimeNs);
-// ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-//
-// // End of first bucket
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 4));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
-// ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-//
-// valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
-// ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(5, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-//
-// valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
-//
-// // Bucket should have been completed.
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10});
-//}
-//
-//TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_use_diff(false);
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
-//
-// allData.clear();
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// // Bucket should have been completed.
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs});
-//}
-//
-//TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Initialization.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
-//
-// allData.clear();
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// // Bucket should have been completed.
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs});
-//}
-//
-//TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Initialization.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
-// return true;
-// }))
-// // notifyAppUpgrade.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(
-// ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10));
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
-//
-// // Bucket should have been completed.
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs});
-//}
-//
-//TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // First on condition changed.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
-// return true;
-// }))
-// // Second on condition changed.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 3));
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-// valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
-// valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
-//
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(2, curInterval.value.long_value);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 10));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
-//
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2});
-//}
-//
-//// TODO: b/145705635 fix or delete this test
-//TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // First condition change.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
-// return true;
-// }))
-// // 2nd condition change.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
-// return true;
-// }))
-// // 3rd condition change.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-// valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 3, 10));
-// valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3);
-//
-// allData.clear();
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
-// valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
-//
-// valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8);
-// valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
-//
-// allData.clear();
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 30));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// // There was not global base available so all buckets are invalid.
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
-//}
-//
-//TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Initial pull.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(1);
-// event->write(1);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// ProtoOutputStream output;
-// std::set<string> strSet;
-// valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
-// FAST, &strSet, &output);
-//
-// StatsLogReport report = outputStreamToProto(&output);
-// // Bucket is invalid since we did not pull when dump report was called.
-// EXPECT_EQ(0, report.value_metrics().data_size());
-//}
-//
-//TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Initial pull.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(1);
-// event->write(1);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(2);
-// event->write(2);
-// event->init();
-// allData.push_back(event);
-// valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// ProtoOutputStream output;
-// std::set<string> strSet;
-// valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST,
-// &strSet, &output);
-//
-// StatsLogReport report = outputStreamToProto(&output);
-// // Previous bucket is part of the report.
-// EXPECT_EQ(1, report.value_metrics().data_size());
-// EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num());
-//}
-//
-//TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Initial pull.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(1);
-// event->write(1);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event->write(tagId);
-// event->write(3);
-// event->write(3);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// ProtoOutputStream output;
-// std::set<string> strSet;
-// valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
-// NO_TIME_CONSTRAINTS, &strSet, &output);
-//
-// StatsLogReport report = outputStreamToProto(&output);
-// EXPECT_EQ(1, report.value_metrics().data_size());
-// EXPECT_EQ(1, report.value_metrics().data(0).bucket_info_size());
-// EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
-//}
-//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_use_diff(false);
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 10));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30);
-//
-// // Bucket should have been completed.
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs});
-//}
-//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-// metric.set_use_diff(false);
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // condition becomes true
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(
-// ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
-// return true;
-// }))
-// // condition becomes false
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(
-// ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 50, 20));
-// return true;
-// }));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-// valueProducer->mCondition = ConditionState::kFalse;
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-// valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(20, curInterval.value.long_value);
-//
-// // Now the alarm is delivered. Condition is off though.
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8});
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-// EXPECT_EQ(false, curInterval.hasValue);
-//}
-//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-// metric.set_use_diff(false);
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // condition becomes true
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(
-// ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
-// return true;
-// }));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-// valueProducer->mCondition = ConditionState::kFalse;
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-//
-// // Now the alarm is delivered. Condition is off though.
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 30));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8});
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-// EXPECT_EQ(false, curInterval.hasValue);
-//}
-//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-// metric.set_use_diff(false);
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-// valueProducer->mCondition = ConditionState::kFalse;
-//
-// // Now the alarm is delivered. Condition is off though.
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 30));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// // Condition was always false.
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
-//}
-//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-// metric.set_use_diff(false);
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // condition becomes true
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// data->push_back(
-// ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
-// return true;
-// }))
-// .WillOnce(Return(false));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-// valueProducer->mCondition = ConditionState::kFalse;
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-// valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
-//
-// // Now the alarm is delivered. Condition is off though.
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 30));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// // No buckets, we had a failure.
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
-//}
-//
-///*
-// * Test that DUMP_REPORT_REQUESTED dump reason is logged.
-// *
-// * For the bucket to be marked invalid during a dump report requested,
-// * three things must be true:
-// * - we want to include the current partial bucket
-// * - we need a pull (metric is pulled and condition is true)
-// * - the dump latency must be FAST
-// */
-//
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Condition change to true.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-// event->write("field1");
-// event->write(10);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// // Condition change event.
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
-//
-// // Check dump report.
-// ProtoOutputStream output;
-// std::set<string> strSet;
-// valueProducer->onDumpReport(bucketStartTimeNs + 40, true /* include recent buckets */, true,
-// FAST /* dumpLatency */, &strSet, &output);
-//
-// StatsLogReport report = outputStreamToProto(&output);
-// EXPECT_TRUE(report.has_value_metrics());
-// EXPECT_EQ(0, report.value_metrics().data_size());
-// EXPECT_EQ(1, report.value_metrics().skipped_size());
-//
-// EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
-// report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-// EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40),
-// report.value_metrics().skipped(0).end_bucket_elapsed_millis());
-// EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-//
-// auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
-// EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
-// EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), dropEvent.drop_time_millis());
-//}
-//
-///*
-// * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition
-// * change event (i.e. the condition change occurs in the wrong bucket).
-// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Condition change to true.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50);
-// event->write("field1");
-// event->write(10);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// // Condition change event.
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
-//
-// // Bucket boundary pull.
-// vector<shared_ptr<LogEvent>> allData;
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs);
-// event->write("field1");
-// event->write(15);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
-//
-// // Late condition change event.
-// valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
-//
-// // Check dump report.
-// ProtoOutputStream output;
-// std::set<string> strSet;
-// valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true,
-// NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-//
-// StatsLogReport report = outputStreamToProto(&output);
-// EXPECT_TRUE(report.has_value_metrics());
-// EXPECT_EQ(1, report.value_metrics().data_size());
-// EXPECT_EQ(1, report.value_metrics().skipped_size());
-//
-// EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
-// report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-// EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
-// report.value_metrics().skipped(0).end_bucket_elapsed_millis());
-// EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-//
-// auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
-// EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
-// EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
-//}
-//
-///*
-// * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate
-// * event (i.e. the accumulate events call occurs in the wrong bucket).
-// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Condition change to true.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50);
-// event->write("field1");
-// event->write(10);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// // Dump report requested.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 100);
-// event->write("field1");
-// event->write(15);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// // Condition change event.
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
-//
-// // Bucket boundary pull.
-// vector<shared_ptr<LogEvent>> allData;
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs);
-// event->write("field1");
-// event->write(15);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
-// event->write("field1");
-// event->write(20);
-// event->init();
-// allData.push_back(event);
-//
-// // Late accumulateEvents event.
-// valueProducer->accumulateEvents(allData, bucket2StartTimeNs - 100, bucket2StartTimeNs - 100);
-//
-// // Check dump report.
-// ProtoOutputStream output;
-// std::set<string> strSet;
-// valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true,
-// NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-//
-// StatsLogReport report = outputStreamToProto(&output);
-// EXPECT_TRUE(report.has_value_metrics());
-// EXPECT_EQ(1, report.value_metrics().data_size());
-// EXPECT_EQ(1, report.value_metrics().skipped_size());
-//
-// EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
-// report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-// EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
-// report.value_metrics().skipped(0).end_bucket_elapsed_millis());
-// EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-//
-// auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
-// EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
-// EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
-//}
-//
-///*
-// * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
-// * when a metric is initialized.
-// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Condition change to true.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50);
-// event->write("field1");
-// event->write(10);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// // Dump report requested.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 100);
-// event->write("field1");
-// event->write(15);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithNoInitialCondition(pullerManager,
-// metric);
-//
-// // Condition change event.
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
-//
-// // Check dump report.
-// ProtoOutputStream output;
-// std::set<string> strSet;
-// int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
-// valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
-// NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-//
-// StatsLogReport report = outputStreamToProto(&output);
-// EXPECT_TRUE(report.has_value_metrics());
-// EXPECT_EQ(0, report.value_metrics().data_size());
-// EXPECT_EQ(1, report.value_metrics().skipped_size());
-//
-// EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
-// report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-// EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
-// report.value_metrics().skipped(0).end_bucket_elapsed_millis());
-// EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-//
-// auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
-// EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
-// EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
-//}
-//
-///*
-// * Test that PULL_FAILED dump reason is logged due to a pull failure in
-// * #pullAndMatchEventsLocked.
-// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Condition change to true.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50);
-// event->write("field1");
-// event->write(10);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// // Dump report requested, pull fails.
-// .WillOnce(Return(false));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-//
-// // Condition change event.
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
-//
-// // Check dump report.
-// ProtoOutputStream output;
-// std::set<string> strSet;
-// int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
-// valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
-// NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-//
-// StatsLogReport report = outputStreamToProto(&output);
-// EXPECT_TRUE(report.has_value_metrics());
-// EXPECT_EQ(0, report.value_metrics().data_size());
-// EXPECT_EQ(1, report.value_metrics().skipped_size());
-//
-// EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
-// report.value_metrics().skipped(0).start_bucket_elapsed_millis());
-// EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
-// report.value_metrics().skipped(0).end_bucket_elapsed_millis());
-// EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-//
-// auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
-// EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
-// EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
-//}
-//
+
+TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // First onConditionChanged
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+
+ // Empty pull.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(false, valueProducer->mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // First onConditionChanged
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval& curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+
+ // End of bucket
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ // Data is empty, base should be reset.
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+ EXPECT_EQ(5, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1});
+}
+
+TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.set_condition(StringToId("SCREEN_ON"));
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // First onConditionChanged
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // End of bucket
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 2));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // Key 1 should be reset since in not present in the most pull.
+ EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ auto iterator = valueProducer->mCurrentSlicedBucket.begin();
+ auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
+ EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
+ EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
+ EXPECT_EQ(false, iterator->second[0].hasValue);
+ iterator++;
+ baseInfoIter++;
+ EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
+ EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
+ EXPECT_EQ(false, iterator->second[0].hasValue);
+
+ EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initialization.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
+ // notifyAppUpgrade.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + bucketSizeNs / 2, 10));
+ return true;
+ }));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+ ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
+
+ valueProducer->notifyAppUpgrade(bucketStartTimeNs + bucketSizeNs / 2, "com.foo", 10000, 1);
+ ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size());
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4));
+ valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1);
+ ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
+}
+
+TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Second onConditionChanged.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
+ return true;
+ }))
+ // Third onConditionChanged.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ valueProducer->mCondition = ConditionState::kUnknown;
+
+ valueProducer->onConditionChanged(false, bucketStartTimeNs);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // End of first bucket
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 4));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(5, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10});
+}
+
+TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_use_diff(false);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs});
+}
+
+TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initialization.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs});
+}
+
+TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initialization.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
+ // notifyAppUpgrade.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 2, 10));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs});
+}
+
+TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // First on condition changed.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
+ // Second on condition changed.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(2, curInterval.value.long_value);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
+
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2});
+}
+
+// TODO: b/145705635 fix or delete this test
+TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // First condition change.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
+ return true;
+ }))
+ // 2nd condition change.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
+ return true;
+ }))
+ // 3rd condition change.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 3, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
+ valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
+
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8);
+ valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // There was not global base available so all buckets are invalid.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
+}
+
+TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initial pull.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
+ FAST, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ // Bucket is invalid since we did not pull when dump report was called.
+ EXPECT_EQ(0, report.value_metrics().data_size());
+}
+
+TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initial pull.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, tagId, 2, 2));
+ valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ // Previous bucket is part of the report.
+ EXPECT_EQ(1, report.value_metrics().data_size());
+ EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num());
+}
+
+TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initial pull.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(
+ CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10, tagId, 3, 3));
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_EQ(1, report.value_metrics().data_size());
+ EXPECT_EQ(1, report.value_metrics().data(0).bucket_info_size());
+ EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
+}
+
+TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_use_diff(false);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30);
+
+ // Bucket should have been completed.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs});
+}
+
+TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+ metric.set_use_diff(false);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // condition becomes true
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
+ return true;
+ }))
+ // condition becomes false
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
+ return true;
+ }));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ valueProducer->mCondition = ConditionState::kFalse;
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(20, curInterval.value.long_value);
+
+ // Now the alarm is delivered. Condition is off though.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8});
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
+}
+
+TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+ metric.set_use_diff(false);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // condition becomes true
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
+ return true;
+ }));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ valueProducer->mCondition = ConditionState::kFalse;
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+
+ // Now the alarm is delivered. Condition is off though.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8});
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
+}
+
+TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+ metric.set_use_diff(false);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ valueProducer->mCondition = ConditionState::kFalse;
+
+ // Now the alarm is delivered. Condition is off though.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // Condition was always false.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
+}
+
+TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+ metric.set_use_diff(false);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // condition becomes true
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
+ return true;
+ }))
+ .WillOnce(Return(false));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ valueProducer->mCondition = ConditionState::kFalse;
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
+
+ // Now the alarm is delivered. Condition is off though.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // No buckets, we had a failure.
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
+}
+
+/*
+ * Test that DUMP_REPORT_REQUESTED dump reason is logged.
+ *
+ * For the bucket to be marked invalid during a dump report requested,
+ * three things must be true:
+ * - we want to include the current partial bucket
+ * - we need a pull (metric is pulled and condition is true)
+ * - the dump latency must be FAST
+ */
+
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 40, true /* include recent buckets */, true,
+ FAST /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ EXPECT_EQ(0, report.value_metrics().data_size());
+ EXPECT_EQ(1, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition
+ * change event (i.e. the condition change occurs in the wrong bucket).
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+
+ // Bucket boundary pull.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ // Late condition change event.
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ EXPECT_EQ(1, report.value_metrics().data_size());
+ EXPECT_EQ(1, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate
+ * event (i.e. the accumulate events call occurs in the wrong bucket).
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
+ return true;
+ }))
+ // Dump report requested.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+
+ // Bucket boundary pull.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 20));
+
+ // Late accumulateEvents event.
+ valueProducer->accumulateEvents(allData, bucket2StartTimeNs - 100, bucket2StartTimeNs - 100);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ EXPECT_EQ(1, report.value_metrics().data_size());
+ EXPECT_EQ(1, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
+ * when a metric is initialized.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
+ return true;
+ }))
+ // Dump report requested.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithNoInitialCondition(pullerManager,
+ metric);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ EXPECT_EQ(0, report.value_metrics().data_size());
+ EXPECT_EQ(1, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that PULL_FAILED dump reason is logged due to a pull failure in
+ * #pullAndMatchEventsLocked.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
+ return true;
+ }))
+ // Dump report requested, pull fails.
+ .WillOnce(Return(false));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ EXPECT_EQ(0, report.value_metrics().data_size());
+ EXPECT_EQ(1, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
+}
+
///*
// * Test that MULTIPLE_BUCKETS_SKIPPED dump reason is logged when a log event
// * skips over more than one bucket.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3624,7 +3419,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Condition change event.
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
@@ -3635,7 +3431,8 @@
// // Check dump report.
// ProtoOutputStream output;
// std::set<string> strSet;
-// valueProducer->onDumpReport(bucket4StartTimeNs + 1000, true /* include recent buckets */, true,
+// valueProducer->onDumpReport(bucket4StartTimeNs + 1000, true /* include recent buckets */,
+// true,
// NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
//
// StatsLogReport report = outputStreamToProto(&output);
@@ -3658,7 +3455,7 @@
// * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
// * is smaller than the "min_bucket_size_nanos" specified in the metric config.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
// metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
//
@@ -3687,7 +3484,8 @@
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Condition change event.
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
@@ -3718,7 +3516,7 @@
///*
// * Test multiple bucket drop events in the same bucket.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3772,7 +3570,7 @@
// * Test that the number of logged bucket drop events is capped at the maximum.
// * The maximum is currently 10 and is set in MetricProducer::maxDropEventsReached().
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3800,10 +3598,8 @@
// .WillOnce(Return(false))
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 220);
-// event->write("field1");
-// event->write(10);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs +
+// 220); event->write("field1"); event->write(10); event->init();
// data->push_back(event);
// return true;
// }));
@@ -3896,7 +3692,7 @@
// * - Using diff
// * - Second field is value field
// */
-//TEST(ValueMetricProducerTest, TestSlicedState) {
+// TEST(ValueMetricProducerTest, TestSlicedState) {
// // Set up ValueMetricProducer.
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3987,7 +3783,8 @@
// EXPECT_EQ(2, it->second[0].value.long_value);
//
// // Bucket status after screen state change ON->OFF.
-// screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+// screenEvent =
+// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
// bucketStartTimeNs + 10);
// StateManager::getInstance().onLogEvent(*screenEvent);
// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4067,9 +3864,10 @@
// * - Using diff
// * - Second field is value field
// */
-//TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
+// TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
// // Set up ValueMetricProducer.
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
+// ValueMetric metric =
+// ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// EXPECT_CALL(*pullerManager, Pull(tagId, _))
// // ValueMetricProducer initialized.
@@ -4189,7 +3987,8 @@
// EXPECT_EQ(2, it->second[0].value.long_value);
//
// // Bucket status after screen state change VR->OFF.
-// screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+// screenEvent =
+// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
// bucketStartTimeNs + 15);
// StateManager::getInstance().onLogEvent(*screenEvent);
// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4243,9 +4042,10 @@
// * - Using diff
// * - Second field is value field
// */
-//TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
+// TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
// // Set up ValueMetricProducer.
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
+// ValueMetric metric =
+// ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
//
@@ -4311,10 +4111,8 @@
// // Uid 1 process state change from Foreground -> Background
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
-// event->write(1 /* uid */);
-// event->write(13);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 20); event->write(1 /* uid */); event->write(13); event->init();
// data->push_back(event);
//
// // This event should be skipped.
@@ -4328,10 +4126,8 @@
// // Uid 1 process state change from Background -> Foreground
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 40);
-// event->write(1 /* uid */);
-// event->write(17);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 40); event->write(1 /* uid */); event->write(17); event->init();
// data->push_back(event);
//
// // This event should be skipped.
@@ -4345,10 +4141,8 @@
// // Dump report pull.
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
-// event->write(2 /* uid */);
-// event->write(20);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 50); event->write(2 /* uid */); event->write(20); event->init();
// data->push_back(event);
//
// event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
@@ -4393,7 +4187,8 @@
//
// // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
// auto uidProcessEvent = CreateUidProcessStateChangedEvent(
-// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs + 20);
+// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs +
+// 20);
// StateManager::getInstance().onLogEvent(*uidProcessEvent);
// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// // Base for dimension key {uid 1}.
@@ -4420,7 +4215,8 @@
//
// // Bucket status after uid 2 process state change kStateUnknown -> Background.
// uidProcessEvent = CreateUidProcessStateChangedEvent(
-// 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs + 40);
+// 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs +
+// 40);
// StateManager::getInstance().onLogEvent(*uidProcessEvent);
// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// // Base for dimension key {uid 1}.
@@ -4499,7 +4295,8 @@
//
// // Bucket status after uid 1 process state change from Foreground -> Background.
// uidProcessEvent = CreateUidProcessStateChangedEvent(
-// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs + 20);
+// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs +
+// 20);
// StateManager::getInstance().onLogEvent(*uidProcessEvent);
//
// EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4533,7 +4330,8 @@
//
// // Bucket status after uid 1 process state change Background->Foreground.
// uidProcessEvent = CreateUidProcessStateChangedEvent(
-// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs + 40);
+// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs +
+// 40);
// StateManager::getInstance().onLogEvent(*uidProcessEvent);
//
// EXPECT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index e2eee03..58eeed3 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -410,6 +410,127 @@
return dimensions;
}
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+
+ return logEvent;
+}
+//
+void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_writeInt32(statsEvent, value3);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+
+ return logEvent;
+}
+
+void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_writeInt32(statsEvent, value3);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+
+ return logEvent;
+}
+
+void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
+ int32_t value) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+
+ return logEvent;
+}
+
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 4371015..4c25ca3 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -164,6 +164,29 @@
FieldMatcher CreateAttributionUidDimensions(const int atomId,
const std::vector<Position>& positions);
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2);
+
+void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2);
+
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3);
+
+void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3);
+
+// The repeated value log event helpers create a log event with two int fields, both
+// set to the same value. This is useful for testing metrics that are only interested
+// in the value of the second field but still need the first field to be populated.
+std::shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs,
+ int32_t value);
+
+void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
+ int32_t value);
+
+std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs);
+
// Create log event for screen state changed.
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state);
diff --git a/config/hiddenapi-greylist-max-q.txt b/config/hiddenapi-greylist-max-q.txt
index a895a44..4832dd1 100644
--- a/config/hiddenapi-greylist-max-q.txt
+++ b/config/hiddenapi-greylist-max-q.txt
@@ -406,7 +406,6 @@
Lcom/android/internal/R$string;->enable_explore_by_touch_warning_title:I
Lcom/android/internal/R$string;->gigabyteShort:I
Lcom/android/internal/R$string;->kilobyteShort:I
-Lcom/android/internal/R$string;->map:I
Lcom/android/internal/R$string;->megabyteShort:I
Lcom/android/internal/R$string;->notification_title:I
Lcom/android/internal/R$string;->no_matches:I
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3a3eea9..e7036bb 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -48,6 +48,7 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.KeyEvent;
+import android.view.SurfaceControl;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -1937,8 +1938,8 @@
* to declare the capability to take screenshot by setting the
* {@link android.R.styleable#AccessibilityService_canTakeScreenshot}
* property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
- * Besides, This API is only supported for default display now
- * {@link Display#DEFAULT_DISPLAY}.
+ * This API only will support {@link Display#DEFAULT_DISPLAY} until {@link SurfaceControl}
+ * supports non-default displays.
* </p>
*
* @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for
@@ -1948,11 +1949,17 @@
*
* @return {@code true} if the taking screenshot accepted, {@code false} if too little time
* has elapsed since the last screenshot, invalid display or internal errors.
+ * @throws IllegalArgumentException if displayId is not {@link Display#DEFAULT_DISPLAY}.
*/
public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<ScreenshotResult> callback) {
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(callback, "callback cannot be null");
+
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException("DisplayId isn't the default display");
+ }
+
final IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance().getConnection(
mConnectionId);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index ca37e9b..2c41e8d 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -18,6 +18,7 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -269,6 +270,11 @@
private float mDurationScale = -1f;
/**
+ * Animation handler used to schedule updates for this animation.
+ */
+ private AnimationHandler mAnimationHandler;
+
+ /**
* Public constants
*/
@@ -1684,6 +1690,15 @@
* @hide
*/
public AnimationHandler getAnimationHandler() {
- return AnimationHandler.getInstance();
+ return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance();
+ }
+
+ /**
+ * Sets the animation handler used to schedule updates for this animator or {@code null} to use
+ * the default handler.
+ * @hide
+ */
+ public void setAnimationHandler(@Nullable AnimationHandler animationHandler) {
+ mAnimationHandler = animationHandler;
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8d6bc72..6480a6a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5829,7 +5829,7 @@
intent.prepareToLeaveProcess(this);
result = ActivityTaskManager.getService()
.startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
- getFeatureId(), intent,
+ getAttributionTag(), intent,
intent.resolveTypeIfNeeded(getContentResolver()), mToken, mEmbeddedID,
requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED, null, options);
} catch (RemoteException e) {
@@ -6624,12 +6624,10 @@
String packageName = getPackageName();
try {
data.prepareToLeaveProcess(this);
- IIntentSender target =
- ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getFeatureId(),
- mParent == null ? mToken : mParent.mToken,
- mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null,
- getUserId());
+ IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
+ ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getAttributionTag(),
+ mParent == null ? mToken : mParent.mToken, mEmbeddedID, requestCode,
+ new Intent[]{data}, null, flags, null, getUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
// Empty
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index dd4788e..f4ee8fa 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1315,7 +1315,8 @@
/**
* @return The in-memory or loaded icon that represents the current state of this task.
- * @deprecated This call is no longer supported.
+ * @deprecated This call is no longer supported. The caller should keep track of any icons
+ * it sets for the task descriptions internally.
*/
@Deprecated
public Bitmap getIcon() {
@@ -4242,7 +4243,6 @@
* {@code false} otherwise.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION)
public boolean updateMccMncConfiguration(@NonNull String mcc, @NonNull String mnc) {
if (mcc == null || mnc == null) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ec11043..f926075 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -34,6 +34,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Activity manager local system service interface.
@@ -124,6 +125,12 @@
public abstract int getUidProcessState(int uid);
/**
+ * Get a map of pid and package name that process of that pid Android/data and Android/obb
+ * directory is not mounted to lowerfs.
+ */
+ public abstract Map<Integer, String> getProcessesWithPendingBindMounts(int userId);
+
+ /**
* @return {@code true} if system is ready, {@code false} otherwise.
*/
public abstract boolean isSystemReady();
@@ -241,7 +248,8 @@
/**
* Returns whether the given user requires credential entry at this time. This is used to
- * intercept activity launches for work apps when the Work Challenge is present.
+ * intercept activity launches for locked work apps due to work challenge being triggered or
+ * when the profile user is yet to be unlocked.
*/
public abstract boolean shouldConfirmCredentials(@UserIdInt int userId);
diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md
new file mode 100644
index 0000000..bee701a
--- /dev/null
+++ b/core/java/android/app/AppOps.md
@@ -0,0 +1,212 @@
+<!--
+ 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
+ -->
+
+# App-ops
+
+App-ops are used for two purposes: Access control and tracking.
+
+App-ops cover a wide variety of functionality from helping with runtime permissions to battery
+consumption tracking.
+
+App-ops are defined in `AppOpsManager` as `OP_...` and need to be continuously numbered. The
+integer values of the app-ops are not exposed. For app-ops visible to 3rd party apps,
+the name of the app-op might be exposed as `OPSTR_`. As the integers are not part of the API, they
+might (and have) changed between platform versions and OEM implementations.
+`AppOpsManager.opToPublicName` and `AppOpsManager.strOpToOp` allow for conversion between integer
+and string identifier for the op.
+
+## App-ops as access restrictions
+
+App-ops can either be controlled for each [uid](../os/Users.md#int-uid) or for each package. Which
+one is used depends on the API provider maintaining this app-op.
+
+For any security or privacy related app-ops the provider needs to control the app-op per uid
+as all security and privacy is based on uid in Android.
+
+App-op used for non-security related tasks are usually controlled per package to provide finer
+granularity.
+
+### Setting the app-op mode
+
+To control access the app-op can be set to:
+
+`MODE_DEFAULT`
+: Default behavior, might differ from app-op to app-op
+
+`MODE_ALLOWED`
+: Allow the access
+
+`MODE_FOREGROUND`
+: Allow the access but only if the app is currently in the [foreground](#foreground)
+
+`MODE_IGNORED`
+: Don't allow the access, i.e. don't perform the requested action or return dummy data
+
+`MODE_ERRORED`
+: Throw a `SecurityException` on access. This can be suppressed by using a `...noThrow` method to
+check the mode
+
+The initial state of an app-op is defined in `AppOpsManager.sOpDefaultMode`. Confusingly the
+initial state is often not `MODE_DEFAULT`
+
+Per-package modes can be set using `AppOpsManager.setMode` and per-uid modes can be set using
+`AppOpsManager.setUidMode`.
+
+**Warning**: Do not use `setMode` and `setUidMode` for the same app-op. Due to the way the
+internal storage for the mode works this can lead to very confusing behavior. If this ever happened
+by accident this needs to be cleaned up for any affected user as the app-op mode is retained over
+reboot.
+
+App-ops can also be set via the shell using the `appops set` command. The target package/uid can be
+defined via parameters to this command.
+
+The current state of the app-op can be read via the `appops get` command or via `dumpsys appops`.
+If the app-op is not mentioned in the output the app-op is in it's initial state.
+
+For example `dumpsys appops`:
+```
+[...]
+ Uid 2000:
+ [...]
+ COARSE_LOCATION: mode=foreground
+ START_FOREGROUND: mode=foreground
+ LEGACY_STORAGE: mode=ignore
+ [...]
+```
+
+### Guarding access based on app-ops
+
+API providers need to check the mode returned by `AppOpsManager.noteOp` if they are are allowing
+access to operations gated by the app-op. `AppOpsManager.unsafeCheckOp` should be used to check the
+mode if no access is granted. E.g. this can be for displaying app-op state in the UI or when
+checking the state before later calling `noteOp` anyway.
+
+If an operation refers to a time span (e.g. a audio-recording session) the API provider should
+use `AppOpsManager.startOp` and `AppOpsManager.finishOp` instead of `noteOp`.
+
+`noteOp` and `startOp` take a `packageName` and `featureId` parameter. These need to be read from
+the calling apps context as `Context.getOpPackageName` and `Context.getFeatureId`, then send to
+the data provider and then passed on the `noteOp`/`startOp` method.
+
+#### App-ops and permissions
+
+Access guarding is often done in combination with permissions using [runtime permissions
+](../permission/Permissions.md#runtime-permissions-and-app-ops) or [app-op permissions
+](../permission/Permissions.md#app-op-permissions). This is preferred over just using an app-op
+ as permissions a concept more familiar to app developers.
+
+### Foreground
+
+The `AppOpsService` tracks the apps' proc state (== foreground-ness) by following the
+`ActivityManagerService`'s proc state. It reduces the possible proc states to only those needed
+for app-ops. It also delays the changes by a _settle time_. This delay is needed as the proc state
+can fluctuate when switching apps. By delaying the change the appops service is not affected by
+those.
+
+The proc state is used for two use cases: Firstly, Tracking remembers the proc state for each
+tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to `MODE_FOREGROUND` are
+translated using the `AppOpsService.UidState.evalMode` method into `MODE_ALLOWED` when the app is
+counted as foreground and `MODE_IGNORED` when the app is counted as background. `checkOpRaw`
+calls are not affected.
+
+The current proc state for an app can be read from `dumpsys appops`. The tracking information can
+be read from `dumpsys appops`
+
+```
+Uid u0a118:
+ state=fg
+ capability=6
+```
+
+## App-ops for tracking
+
+App-ops track many important events, including all accesses to runtime permission protected
+APIs. This is done by tracking when an app-op was noted or started. The tracked data can only be
+read by system components.
+
+**Note:** Only `noteOp`/`startOp` calls are tracked; `unsafeCheckOp` is not tracked. Hence it is
+important to eventually call `noteOp` or `startOp` when providing access to protected operations
+or data.
+
+Some apps are forwarding access to other apps. E.g. an app might get the location from the
+system's location provider and then send the location further to a 3rd app. In this case the
+app passing on the data needs to call `AppOpsManager.noteProxyOp` to signal the access proxying.
+This might also make sense inside of a single app if the access is forwarded between two features of
+the app. In this case an app-op is noted for the forwarding app (proxy) and the app that received
+the data (proxied). As any app can do it is important to track how much the system trusts this
+proxy-access-tracking. For more details see `AppOpService.noteProxyOperation`.
+
+The tracking information can be read from `dumpsys appops` split by feature, proc state and
+proxying information with the syntax
+
+```
+Package THE_PACKAGE_NAME:
+ AN_APP_OP (CURRENT_MODE):
+ FEATURE_ID (or null for default feature)=[
+ ACCESS_OR_REJECT: [PROC_STATE-PROXYING_TAG] TIME proxy[INFO_ABOUT_PROXY IF_PROXY_ACCESS]
+```
+
+Example:
+
+```
+Package com.google.android.gms:
+ READ_CONTACTS (allow):
+ null=[
+ Access: [fgsvc-s] 2020-02-14 14:24:10.559 (-3d23h15m43s642ms)
+ Access: [fgsvc-tp] 2020-02-14 14:23:58.189 (-3d23h15m56s12ms)
+ ]
+ apkappcontext=[
+ Access: [fg-tp] 2020-02-17 14:24:54.721 (-23h14m59s480ms)
+ ]
+ com.google.android.gms.icing=[
+ Access: [fgsvc-tpd] 2020-02-14 14:26:27.018 (-3d23h13m27s183ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
+ Access: [fg-tpd] 2020-02-18 02:26:08.711 (-11h13m45s490ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
+ Access: [bg-tpd] 2020-02-14 14:34:55.310 (-3d23h4m58s891ms) proxy[uid=10070, pkg=com.android.providers.contacts, feature=null]
+ ]
+ MANAGE_EXTERNAL_STORAGE (default):
+ null=[
+ Reject: [fg-s]2020-02-18 08:00:04.444 (-5h39m49s757ms)
+ Reject: [bg-s]2020-02-18 08:00:04.427 (-5h39m49s774ms)
+ ]
+```
+
+### Tracking an app's own private data accesses
+
+An app can register an `AppOpsManager.OnOpNotedCallback` to get informed about what accesses the
+system is tracking for it. As each runtime permission has an associated app-op this API is
+particularly useful for an app that want to find unexpected private data accesses.
+
+## Listening to app-op events
+
+System apps (with the appropriate permissions) can listen to most app-op events, such as
+
+`noteOp`
+: `startWatchingNoted`
+
+`startOp`/`finishOp`
+: `startWatchingActive`
+
+mode changes
+: `startWatchingMode`
+
+[foreground](#foreground)-ness changes
+: `startWatchingMode` using the `WATCH_FOREGROUND_CHANGES` flag
+
+Watching such events is only ever as good as the tracked events. E.g. if the audio provider does
+not call `startOp` for a audio-session, the app's activeness for the record-audio app-op is not
+changed. Further there were cases where app-ops were noted even though no data was accessed or
+operation was performed. Hence before relying on the data from app-ops, double check if the data
+is actually reliable.
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6564dc9..fa4aa19 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -27,6 +27,7 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.usage.UsageStatsManager;
+import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
@@ -159,8 +160,8 @@
* <p>Some apps are forwarding access to other apps. E.g. an app might get the location from the
* system's location provider and then send the location further to a 3rd app. In this case the
* app passing on the data needs to call {@link #noteProxyOp} to signal the access proxying. This
- * might also make sense inside of a single app if the access is forwarded between two features of
- * the app.
+ * might also make sense inside of a single app if the access is forwarded between two parts of
+ * the tagged with different attribution tags.
*
* <p>An app can register an {@link OnOpNotedCallback} to get informed about what accesses the
* system is tracking for it. As each runtime permission has an associated app-op this API is
@@ -385,6 +386,13 @@
*/
public static final int WATCH_FOREGROUND_CHANGES = 1 << 0;
+ /**
+ * Flag for {@link #startWatchingMode} that causes the callback to happen on the switch-op
+ * instead the op the callback was registered. (This simulates pre-R behavior).
+ *
+ * @hide
+ */
+ public static final int CALL_BACK_ON_SWITCHED_OP = 1 << 1;
/**
* Flag to determine whether we should log noteOp/startOp calls to make sure they
@@ -688,6 +696,10 @@
public static final int SAMPLING_STRATEGY_RARELY_USED =
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
+ /** @hide */
+ public static final int SAMPLING_STRATEGY_BOOT_TIME_SAMPLING =
+ FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__BOOT_TIME_SAMPLING;
+
/**
* Strategies used for message sampling
* @hide
@@ -696,7 +708,8 @@
@IntDef(prefix = {"SAMPLING_STRATEGY_"}, value = {
SAMPLING_STRATEGY_DEFAULT,
SAMPLING_STRATEGY_UNIFORM,
- SAMPLING_STRATEGY_RARELY_USED
+ SAMPLING_STRATEGY_RARELY_USED,
+ SAMPLING_STRATEGY_BOOT_TIME_SAMPLING
})
public @interface SamplingStrategy {}
@@ -1063,9 +1076,17 @@
/** @hide Auto-revoke app permissions if app is unused for an extended period */
public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
+ /**
+ * Whether {@link #OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED} is allowed to be changed by
+ * the installer
+ *
+ * @hide
+ */
+ public static final int OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98;
+
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 98;
+ public static final int _NUM_OP = 99;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1348,7 +1369,6 @@
@SystemApi
public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
/** @hide Read device identifiers */
- @SystemApi
public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
/** @hide Query all packages on device */
public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
@@ -1365,6 +1385,10 @@
public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED =
"android:auto_revoke_permissions_if_unused";
+ /** @hide Auto-revoke app permissions if app is unused for an extended period */
+ public static final String OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER =
+ "android:auto_revoke_managed_by_installer";
+
/** @hide Communicate cross-profile within the same profile group. */
@SystemApi
public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
@@ -1455,6 +1479,7 @@
OP_LOADER_USAGE_STATS,
OP_ACCESS_CALL_AUDIO,
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
};
/**
@@ -1564,6 +1589,7 @@
OP_LOADER_USAGE_STATS, // LOADER_USAGE_STATS
OP_ACCESS_CALL_AUDIO, // ACCESS_CALL_AUDIO
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -1668,6 +1694,7 @@
OPSTR_LOADER_USAGE_STATS,
OPSTR_ACCESS_CALL_AUDIO,
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
};
/**
@@ -1773,6 +1800,7 @@
"LOADER_USAGE_STATS",
"ACCESS_CALL_AUDIO",
"AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
+ "AUTO_REVOKE_MANAGED_BY_INSTALLER",
};
/**
@@ -1879,6 +1907,7 @@
android.Manifest.permission.LOADER_USAGE_STATS,
Manifest.permission.ACCESS_CALL_AUDIO,
null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -1985,6 +2014,7 @@
null, // LOADER_USAGE_STATS
null, // ACCESS_CALL_AUDIO
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -2090,6 +2120,7 @@
null, // LOADER_USAGE_STATS
null, // ACCESS_CALL_AUDIO
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -2194,6 +2225,7 @@
AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
AppOpsManager.MODE_DEFAULT, // ACCESS_CALL_AUDIO
AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -2302,9 +2334,118 @@
false, // LOADER_USAGE_STATS
false, // ACCESS_CALL_AUDIO
false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
+ * This maps each operation to its statsd logging code.
+ */
+ private static int[] sOpToLoggingId = new int[]{
+ AppProtoEnums.APP_OP_COARSE_LOCATION, // OP_COARSE_LOCATION
+ AppProtoEnums.APP_OP_FINE_LOCATION, // OP_FINE_LOCATION
+ AppProtoEnums.APP_OP_GPS, // OP_ID__GPS
+ AppProtoEnums.APP_OP_VIBRATE, // OP_VIBRATE
+ AppProtoEnums.APP_OP_READ_CONTACTS, // OP_READ_CONTACTS
+ AppProtoEnums.APP_OP_WRITE_CONTACTS, // OP_WRITE_CONTACTS
+ AppProtoEnums.APP_OP_READ_CALL_LOG, // OP_READ_CALL_LOG
+ AppProtoEnums.APP_OP_WRITE_CALL_LOG, // OP_WRITE_CALL_LOG
+ AppProtoEnums.APP_OP_READ_CALENDAR, // OP_READ_CALENDAR
+ AppProtoEnums.APP_OP_WRITE_CALENDAR, // OP_WRITE_CALENDAR
+ AppProtoEnums.APP_OP_WIFI_SCAN, // OP_WIFI_SCAN
+ AppProtoEnums.APP_OP_POST_NOTIFICATION, // OP_POST_NOTIFICATION
+ AppProtoEnums.APP_OP_NEIGHBORING_CELLS, // OP_NEIGHBORING_CELLS
+ AppProtoEnums.APP_OP_CALL_PHONE, // OP_CALL_PHONE
+ AppProtoEnums.APP_OP_READ_SMS, // OP_READ_SMS
+ AppProtoEnums.APP_OP_WRITE_SMS, // OP_WRITE_SMS
+ AppProtoEnums.APP_OP_RECEIVE_SMS, // OP_RECEIVE_SMS
+ AppProtoEnums.APP_OP_RECEIVE_EMERGENCY_SMS, // OP_RECEIVE_EMERGENCY_SMS
+ AppProtoEnums.APP_OP_RECEIVE_MMS, // OP_RECEIVE_MMS
+ AppProtoEnums.APP_OP_RECEIVE_WAP_PUSH, // OP_RECEIVE_WAP_PUSH
+ AppProtoEnums.APP_OP_SEND_SMS, // OP_SEND_SMS
+ AppProtoEnums.APP_OP_READ_ICC_SMS, // OP_READ_ICC_SMS
+ AppProtoEnums.APP_OP_WRITE_ICC_SMS, // OP_WRITE_ICC_SMS
+ AppProtoEnums.APP_OP_WRITE_SETTINGS, // OP_WRITE_SETTINGS
+ AppProtoEnums.APP_OP_SYSTEM_ALERT_WINDOW, // OP_SYSTEM_ALERT_WINDOW
+ AppProtoEnums.APP_OP_ACCESS_NOTIFICATIONS, // OP_ACCESS_NOTIFICATIONS
+ AppProtoEnums.APP_OP_CAMERA, // OP_CAMERA
+ AppProtoEnums.APP_OP_RECORD_AUDIO, // OP_RECORD_AUDIO
+ AppProtoEnums.APP_OP_PLAY_AUDIO, // OP_PLAY_AUDIO
+ AppProtoEnums.APP_OP_READ_CLIPBOARD, // OP_READ_CLIPBOARD
+ AppProtoEnums.APP_OP_WRITE_CLIPBOARD, // OP_WRITE_CLIPBOARD
+ AppProtoEnums.APP_OP_TAKE_MEDIA_BUTTONS, // OP_TAKE_MEDIA_BUTTONS
+ AppProtoEnums.APP_OP_TAKE_AUDIO_FOCUS, // OP_TAKE_AUDIO_FOCUS
+ AppProtoEnums.APP_OP_AUDIO_MASTER_VOLUME, // OP_AUDIO_MASTER_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_VOICE_VOLUME, // OP_AUDIO_VOICE_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_RING_VOLUME, // OP_AUDIO_RING_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_MEDIA_VOLUME, // OP_AUDIO_MEDIA_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_ALARM_VOLUME, // OP_AUDIO_ALARM_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_NOTIFICATION_VOLUME, // OP_AUDIO_NOTIFICATION_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_BLUETOOTH_VOLUME, // OP_AUDIO_BLUETOOTH_VOLUME
+ AppProtoEnums.APP_OP_WAKE_LOCK, // OP_WAKE_LOCK
+ AppProtoEnums.APP_OP_MONITOR_LOCATION, // OP_MONITOR_LOCATION
+ AppProtoEnums.APP_OP_MONITOR_HIGH_POWER_LOCATION, // OP_MONITOR_HIGH_POWER_LOCATION
+ AppProtoEnums.APP_OP_GET_USAGE_STATS, // OP_GET_USAGE_STATS
+ AppProtoEnums.APP_OP_MUTE_MICROPHONE, //OP_MUTE_MICROPHONE
+ AppProtoEnums.APP_OP_TOAST_WINDOW, // OP_TOAST_WINDOW
+ AppProtoEnums.APP_OP_PROJECT_MEDIA, // OP_PROJECT_MEDIA
+ AppProtoEnums.APP_OP_ACTIVATE_VPN, // OP_ACTIVATE_VPN
+ AppProtoEnums.APP_OP_WRITE_WALLPAPER, // OP_WRITE_WALLPAPER
+ AppProtoEnums.APP_OP_ASSIST_STRUCTURE, // OP_ASSIST_STRUCTURE
+ AppProtoEnums.APP_OP_ASSIST_SCREENSHOT, // OP_ASSIST_SCREENSHOT
+ AppProtoEnums.APP_OP_READ_PHONE_STATE, // OP_READ_PHONE_STATE
+ AppProtoEnums.APP_OP_ADD_VOICEMAIL, // OP_ADD_VOICEMAIL
+ AppProtoEnums.APP_OP_USE_SIP, // OP_USE_SIP
+ AppProtoEnums.APP_OP_PROCESS_OUTGOING_CALLS, // OP_PROCESS_OUTGOING_CALLS
+ AppProtoEnums.APP_OP_USE_FINGERPRINT, // OP_USE_FINGERPRINT
+ AppProtoEnums.APP_OP_BODY_SENSORS, // OP_BODY_SENSORS
+ AppProtoEnums.APP_OP_READ_CELL_BROADCASTS, // OP_READ_CELL_BROADCASTS
+ AppProtoEnums.APP_OP_MOCK_LOCATION, // OP_MOCK_LOCATION
+ AppProtoEnums.APP_OP_READ_EXTERNAL_STORAGE, // OP_READ_EXTERNAL_STORAGE
+ AppProtoEnums.APP_OP_WRITE_EXTERNAL_STORAGE, // OP_WRITE_EXTERNAL_STORAGE
+ AppProtoEnums.APP_OP_TURN_SCREEN_ON, // OP_TURN_SCREEN_ON
+ AppProtoEnums.APP_OP_GET_ACCOUNTS, // OP_GET_ACCOUNTS
+ AppProtoEnums.APP_OP_RUN_IN_BACKGROUND, // OP_RUN_IN_BACKGROUND
+ AppProtoEnums.APP_OP_AUDIO_ACCESSIBILITY_VOLUME, // OP_AUDIO_ACCESSIBILITY_VOLUME
+ AppProtoEnums.APP_OP_READ_PHONE_NUMBERS, // OP_READ_PHONE_NUMBERS
+ AppProtoEnums.APP_OP_REQUEST_INSTALL_PACKAGES, // OP_REQUEST_INSTALL_PACKAGES
+ AppProtoEnums.APP_OP_PICTURE_IN_PICTURE, // OP_PICTURE_IN_PICTURE
+ AppProtoEnums.APP_OP_INSTANT_APP_START_FOREGROUND, // OP_INSTANT_APP_START_FOREGROUND
+ AppProtoEnums.APP_OP_ANSWER_PHONE_CALLS, // OP_ANSWER_PHONE_CALLS
+ AppProtoEnums.APP_OP_RUN_ANY_IN_BACKGROUND, // OP_RUN_ANY_IN_BACKGROUND
+ AppProtoEnums.APP_OP_CHANGE_WIFI_STATE, // OP_CHANGE_WIFI_STATE
+ AppProtoEnums.APP_OP_REQUEST_DELETE_PACKAGES, // OP_REQUEST_DELETE_PACKAGES
+ AppProtoEnums.APP_OP_BIND_ACCESSIBILITY_SERVICE, // OP_BIND_ACCESSIBILITY_SERVICE
+ AppProtoEnums.APP_OP_ACCEPT_HANDOVER, // OP_ACCEPT_HANDOVER
+ AppProtoEnums.APP_OP_MANAGE_IPSEC_TUNNELS, // OP_MANAGE_IPSEC_TUNNELS
+ AppProtoEnums.APP_OP_START_FOREGROUND, // OP_START_FOREGROUND
+ AppProtoEnums.APP_OP_BLUETOOTH_SCAN, // OP_BLUETOOTH_SCAN
+ AppProtoEnums.APP_OP_USE_BIOMETRIC, // OP_USE_BIOMETRIC
+ AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION, // OP_ACTIVITY_RECOGNITION
+ AppProtoEnums.APP_OP_SMS_FINANCIAL_TRANSACTIONS, // OP_SMS_FINANCIAL_TRANSACTIONS
+ AppProtoEnums.APP_OP_READ_MEDIA_AUDIO, // OP_READ_MEDIA_AUDIO
+ AppProtoEnums.APP_OP_WRITE_MEDIA_AUDIO, // OP_WRITE_MEDIA_AUDIO
+ AppProtoEnums.APP_OP_READ_MEDIA_VIDEO, // OP_READ_MEDIA_VIDEO
+ AppProtoEnums.APP_OP_WRITE_MEDIA_VIDEO, // OP_WRITE_MEDIA_VIDEO
+ AppProtoEnums.APP_OP_READ_MEDIA_IMAGES, // OP_READ_MEDIA_IMAGES
+ AppProtoEnums.APP_OP_WRITE_MEDIA_IMAGES, // OP_WRITE_MEDIA_IMAGES
+ AppProtoEnums.APP_OP_LEGACY_STORAGE, // OP_LEGACY_STORAGE
+ AppProtoEnums.APP_OP_ACCESS_ACCESSIBILITY, // OP_ACCESS_ACCESSIBILITY
+ AppProtoEnums.APP_OP_READ_DEVICE_IDENTIFIERS, // OP_READ_DEVICE_IDENTIFIERS
+ AppProtoEnums.APP_OP_ACCESS_MEDIA_LOCATION, // OP_ACCESS_MEDIA_LOCATION
+ AppProtoEnums.APP_OP_QUERY_ALL_PACKAGES, // OP_QUERY_ALL_PACKAGES
+ AppProtoEnums.APP_OP_MANAGE_EXTERNAL_STORAGE, // OP_MANAGE_EXTERNAL_STORAGE
+ AppProtoEnums.APP_OP_INTERACT_ACROSS_PROFILES, // OP_INTERACT_ACROSS_PROFILES
+ AppProtoEnums.APP_OP_ACTIVATE_PLATFORM_VPN, // OP_ACTIVATE_PLATFORM_VPN
+ AppProtoEnums.APP_OP_LOADER_USAGE_STATS, // OP_LOADER_USAGE_STATS
+ AppProtoEnums.APP_OP_ACCESS_CALL_AUDIO, // OP_ACCESS_CALL_AUDIO
+ AppProtoEnums.APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ AppProtoEnums.APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ };
+
+
+ /**
* Mapping from an app op name to the app op code.
*/
private static HashMap<String, Integer> sOpStrToOp = new HashMap<>();
@@ -2345,6 +2486,10 @@
throw new IllegalStateException("sOpToString length " + sOpToString.length
+ " should be " + _NUM_OP);
}
+ if (sOpToLoggingId.length != _NUM_OP) {
+ throw new IllegalStateException("sOpToLoggingId length " + sOpToLoggingId.length
+ + " should be " + _NUM_OP);
+ }
if (sOpNames.length != _NUM_OP) {
throw new IllegalStateException("sOpNames length " + sOpNames.length
+ " should be " + _NUM_OP);
@@ -2429,6 +2574,15 @@
}
/**
+ * Retrieve a logging id for the operation.
+ *
+ * @hide
+ */
+ public static int opToLoggingId(int op) {
+ return sOpToLoggingId[op];
+ }
+
+ /**
* @hide
*/
public static int strDebugOpToOp(String op) {
@@ -2650,23 +2804,23 @@
private @IntRange(from = 0) int mUid;
/** Package of the proxy that noted the op */
private @Nullable String mPackageName;
- /** ID of the feature of the proxy that noted the op */
- private @Nullable String mFeatureId;
+ /** Attribution tag of the proxy that noted the op */
+ private @Nullable String mAttributionTag;
/**
* Reinit existing object with new state.
*
* @param uid UID of the proxy app that noted the op
* @param packageName Package of the proxy that noted the op
- * @param featureId ID of the feature of the proxy that noted the op
+ * @param attributionTag attribution tag of the proxy that noted the op
*
* @hide
*/
public void reinit(@IntRange(from = 0) int uid, @Nullable String packageName,
- @Nullable String featureId) {
+ @Nullable String attributionTag) {
mUid = Preconditions.checkArgumentNonnegative(uid);
mPackageName = packageName;
- mFeatureId = featureId;
+ mAttributionTag = attributionTag;
}
@@ -2691,21 +2845,21 @@
* UID of the proxy app that noted the op
* @param packageName
* Package of the proxy that noted the op
- * @param featureId
- * ID of the feature of the proxy that noted the op
+ * @param attributionTag
+ * Attribution tag of the proxy that noted the op
* @hide
*/
@DataClass.Generated.Member
public OpEventProxyInfo(
@IntRange(from = 0) int uid,
@Nullable String packageName,
- @Nullable String featureId) {
+ @Nullable String attributionTag) {
this.mUid = uid;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mUid,
"from", 0);
this.mPackageName = packageName;
- this.mFeatureId = featureId;
+ this.mAttributionTag = attributionTag;
// onConstructed(); // You can define this method to get a callback
}
@@ -2719,7 +2873,7 @@
public OpEventProxyInfo(@NonNull OpEventProxyInfo orig) {
mUid = orig.mUid;
mPackageName = orig.mPackageName;
- mFeatureId = orig.mFeatureId;
+ mAttributionTag = orig.mAttributionTag;
}
/**
@@ -2739,11 +2893,11 @@
}
/**
- * ID of the feature of the proxy that noted the op
+ * Attribution tag of the proxy that noted the op
*/
@DataClass.Generated.Member
- public @Nullable String getFeatureId() {
- return mFeatureId;
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
}
@Override
@@ -2754,11 +2908,11 @@
byte flg = 0;
if (mPackageName != null) flg |= 0x2;
- if (mFeatureId != null) flg |= 0x4;
+ if (mAttributionTag != null) flg |= 0x4;
dest.writeByte(flg);
dest.writeInt(mUid);
if (mPackageName != null) dest.writeString(mPackageName);
- if (mFeatureId != null) dest.writeString(mFeatureId);
+ if (mAttributionTag != null) dest.writeString(mAttributionTag);
}
@Override
@@ -2775,14 +2929,14 @@
byte flg = in.readByte();
int uid = in.readInt();
String packageName = (flg & 0x2) == 0 ? null : in.readString();
- String featureId = (flg & 0x4) == 0 ? null : in.readString();
+ String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
this.mUid = uid;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mUid,
"from", 0);
this.mPackageName = packageName;
- this.mFeatureId = featureId;
+ this.mAttributionTag = attributionTag;
// onConstructed(); // You can define this method to get a callback
}
@@ -2806,7 +2960,7 @@
time = 1576814974615L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
- inputSignatures = "private @android.annotation.IntRange(from=0L) int mUid\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.Nullable java.lang.String mFeatureId\npublic void reinit(int,java.lang.String,java.lang.String)\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genHiddenCopyConstructor=true)")
+ inputSignatures = "private @android.annotation.IntRange(from=0L) int mUid\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\npublic void reinit(int,java.lang.String,java.lang.String)\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genHiddenCopyConstructor=true)")
@Deprecated
private void __metadata() {}
*/
@@ -3005,7 +3159,7 @@
/**
* Last {@link #noteOp} and {@link #startOp} events performed for a single op and a specific
- * {@link Context#createFeatureContext(String) feature} for all uidModes and opFlags.
+ * {@link Context#createAttributionContext(String) attribution} for all uidModes and opFlags.
*
* @hide
*/
@@ -3014,7 +3168,7 @@
@Immutable
// @DataClass(genHiddenConstructor = true) codegen verifier is broken
@DataClass.Suppress({"getAccessEvents", "getRejectEvents", "getOp"})
- public static final class OpFeatureEntry implements Parcelable {
+ public static final class AttributedOpEntry implements Parcelable {
/** The code of the op */
private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
/** Whether the op is running */
@@ -3313,8 +3467,8 @@
}
/**
- * Gets the proxy info of the app that performed the last access on behalf of this feature
- * and as a result blamed the op on this app.
+ * Gets the proxy info of the app that performed the last access on behalf of this
+ * attribution and as a result blamed the op on this attribution.
*
* @param flags The op flags
*
@@ -3331,7 +3485,7 @@
/**
* Gets the proxy info of the app that performed the last foreground access on behalf of
- * this feature and as a result blamed the op on this app.
+ * this attribution and as a result blamed the op on this attribution.
*
* @param flags The op flags
*
@@ -3349,7 +3503,7 @@
/**
* Gets the proxy info of the app that performed the last background access on behalf of
- * this feature and as a result blamed the op on this app.
+ * this attribution and as a result blamed the op on this attribution.
*
* @param flags The op flags
*
@@ -3366,8 +3520,8 @@
}
/**
- * Gets the proxy info of the app that performed the last access on behalf of this feature
- * and as a result blamed the op on this app.
+ * Gets the proxy info of the app that performed the last access on behalf of this
+ * attribution and as a result blamed the op on this attribution.
*
* @param fromUidState The lowest UID state for which to query
* @param toUidState The highest UID state for which to query (inclusive)
@@ -3442,7 +3596,7 @@
/**
- * Creates a new OpFeatureEntry.
+ * Creates a new OpAttributionEntry.
*
* @param op
* The code of the op
@@ -3455,7 +3609,7 @@
* @hide
*/
@DataClass.Generated.Member
- public OpFeatureEntry(
+ public AttributedOpEntry(
@IntRange(from = 0, to = _NUM_OP - 1) int op,
boolean running,
@Nullable LongSparseArray<NoteOpEvent> accessEvents,
@@ -3525,7 +3679,7 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ OpFeatureEntry(@NonNull Parcel in) {
+ /* package-private */ AttributedOpEntry(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -3548,16 +3702,16 @@
}
@DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<OpFeatureEntry> CREATOR
- = new Parcelable.Creator<OpFeatureEntry>() {
+ public static final @NonNull Parcelable.Creator<AttributedOpEntry> CREATOR
+ = new Parcelable.Creator<AttributedOpEntry>() {
@Override
- public OpFeatureEntry[] newArray(int size) {
- return new OpFeatureEntry[size];
+ public AttributedOpEntry[] newArray(int size) {
+ return new AttributedOpEntry[size];
}
@Override
- public OpFeatureEntry createFromParcel(@NonNull Parcel in) {
- return new OpFeatureEntry(in);
+ public AttributedOpEntry createFromParcel(@NonNull Parcel in) {
+ return new AttributedOpEntry(in);
}
};
@@ -3566,7 +3720,7 @@
time = 1574809856239L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final boolean mRunning\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpFeatureEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mAccessEvents\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpFeatureEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mRejectEvents\npublic @android.annotation.NonNull android.util.ArraySet<java.lang.Long> collectKeys()\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyFeatureId(int,int)\nclass OpFeatureEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final boolean mRunning\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpAttributionEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mAccessEvents\nprivate final @com.android.internal.util.DataClass.ParcelWith(android.app.OpAttributionEntry.LongSparseArrayParceling.class) @android.annotation.Nullable android.util.LongSparseArray<android.app.NoteOpEvent> mRejectEvents\npublic @android.annotation.NonNull android.util.ArraySet<java.lang.Long> collectKeys()\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\npublic @android.annotation.Nullable java.lang.String getProxyAttributionTag(int,int)\nclass OpAttributionEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
*/
@@ -3592,8 +3746,8 @@
private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
/** The mode of the op */
private final @Mode int mMode;
- /** The features that have been used when checking the op */
- private final @NonNull Map<String, OpFeatureEntry> mFeatures;
+ /** The attributed entries by attribution tag */
+ private final @NonNull Map<String, AttributedOpEntry> mAttributedOpEntries;
/**
* @hide
@@ -3634,7 +3788,7 @@
* @see #getLastAccessForegroundTime(int)
* @see #getLastAccessBackgroundTime(int)
* @see #getLastAccessTime(int, int, int)
- * @see OpFeatureEntry#getLastAccessTime(int)
+ * @see AttributedOpEntry#getLastAccessTime(int)
*/
public long getLastAccessTime(@OpFlags int flags) {
return getLastAccessTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
@@ -3651,7 +3805,7 @@
* @see #getLastAccessTime(int)
* @see #getLastAccessBackgroundTime(int)
* @see #getLastAccessTime(int, int, int)
- * @see OpFeatureEntry#getLastAccessForegroundTime(int)
+ * @see AttributedOpEntry#getLastAccessForegroundTime(int)
*/
public long getLastAccessForegroundTime(@OpFlags int flags) {
return getLastAccessTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
@@ -3669,7 +3823,7 @@
* @see #getLastAccessTime(int)
* @see #getLastAccessForegroundTime(int)
* @see #getLastAccessTime(int, int, int)
- * @see OpFeatureEntry#getLastAccessBackgroundTime(int)
+ * @see AttributedOpEntry#getLastAccessBackgroundTime(int)
*/
public long getLastAccessBackgroundTime(@OpFlags int flags) {
return getLastAccessTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
@@ -3686,13 +3840,14 @@
private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState,
@UidState int toUidState, @OpFlags int flags) {
NoteOpEvent lastAccessEvent = null;
- for (OpFeatureEntry featureEntry : mFeatures.values()) {
- NoteOpEvent lastFeatureAccessEvent = featureEntry.getLastAccessEvent(fromUidState,
- toUidState, flags);
+ for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) {
+ NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastAccessEvent(
+ fromUidState, toUidState, flags);
- if (lastAccessEvent == null || (lastFeatureAccessEvent != null
- && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) {
- lastAccessEvent = lastFeatureAccessEvent;
+ if (lastAccessEvent == null || (lastAttributionAccessEvent != null
+ && lastAttributionAccessEvent.getNoteTime()
+ > lastAccessEvent.getNoteTime())) {
+ lastAccessEvent = lastAttributionAccessEvent;
}
}
@@ -3712,7 +3867,7 @@
* @see #getLastAccessTime(int)
* @see #getLastAccessForegroundTime(int)
* @see #getLastAccessBackgroundTime(int)
- * @see OpFeatureEntry#getLastAccessTime(int, int, int)
+ * @see AttributedOpEntry#getLastAccessTime(int, int, int)
*/
public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
@OpFlags int flags) {
@@ -3748,7 +3903,7 @@
* @see #getLastRejectForegroundTime(int)
* @see #getLastRejectBackgroundTime(int)
* @see #getLastRejectTime(int, int, int)
- * @see OpFeatureEntry#getLastRejectTime(int)
+ * @see AttributedOpEntry#getLastRejectTime(int)
*/
public long getLastRejectTime(@OpFlags int flags) {
return getLastRejectTime(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
@@ -3765,7 +3920,7 @@
* @see #getLastRejectTime(int)
* @see #getLastRejectBackgroundTime(int)
* @see #getLastRejectTime(int, int, int)
- * @see OpFeatureEntry#getLastRejectForegroundTime(int)
+ * @see AttributedOpEntry#getLastRejectForegroundTime(int)
*/
public long getLastRejectForegroundTime(@OpFlags int flags) {
return getLastRejectTime(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
@@ -3783,7 +3938,7 @@
* @see #getLastRejectTime(int)
* @see #getLastRejectForegroundTime(int)
* @see #getLastRejectTime(int, int, int)
- * @see OpFeatureEntry#getLastRejectBackgroundTime(int)
+ * @see AttributedOpEntry#getLastRejectBackgroundTime(int)
*/
public long getLastRejectBackgroundTime(@OpFlags int flags) {
return getLastRejectTime(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
@@ -3800,13 +3955,14 @@
private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState,
@UidState int toUidState, @OpFlags int flags) {
NoteOpEvent lastAccessEvent = null;
- for (OpFeatureEntry featureEntry : mFeatures.values()) {
- NoteOpEvent lastFeatureAccessEvent = featureEntry.getLastRejectEvent(fromUidState,
- toUidState, flags);
+ for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) {
+ NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastRejectEvent(
+ fromUidState, toUidState, flags);
- if (lastAccessEvent == null || (lastFeatureAccessEvent != null
- && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) {
- lastAccessEvent = lastFeatureAccessEvent;
+ if (lastAccessEvent == null || (lastAttributionAccessEvent != null
+ && lastAttributionAccessEvent.getNoteTime()
+ > lastAccessEvent.getNoteTime())) {
+ lastAccessEvent = lastAttributionAccessEvent;
}
}
@@ -3827,7 +3983,7 @@
* @see #getLastRejectForegroundTime(int)
* @see #getLastRejectBackgroundTime(int)
* @see #getLastRejectTime(int, int, int)
- * @see OpFeatureEntry#getLastRejectTime(int, int, int)
+ * @see AttributedOpEntry#getLastRejectTime(int, int, int)
*/
public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState,
@OpFlags int flags) {
@@ -3843,8 +3999,8 @@
* @return Whether the operation is running.
*/
public boolean isRunning() {
- for (OpFeatureEntry opFeatureEntry : mFeatures.values()) {
- if (opFeatureEntry.isRunning()) {
+ for (AttributedOpEntry opAttributionEntry : mAttributedOpEntries.values()) {
+ if (opAttributionEntry.isRunning()) {
return true;
}
}
@@ -3870,7 +4026,7 @@
* @see #getLastForegroundDuration(int)
* @see #getLastBackgroundDuration(int)
* @see #getLastDuration(int, int, int)
- * @see OpFeatureEntry#getLastDuration(int)
+ * @see AttributedOpEntry#getLastDuration(int)
*/
public long getLastDuration(@OpFlags int flags) {
return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
@@ -3886,7 +4042,7 @@
* @see #getLastDuration(int)
* @see #getLastBackgroundDuration(int)
* @see #getLastDuration(int, int, int)
- * @see OpFeatureEntry#getLastForegroundDuration(int)
+ * @see AttributedOpEntry#getLastForegroundDuration(int)
*/
public long getLastForegroundDuration(@OpFlags int flags) {
return getLastDuration(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
@@ -3903,7 +4059,7 @@
* @see #getLastDuration(int)
* @see #getLastForegroundDuration(int)
* @see #getLastDuration(int, int, int)
- * @see OpFeatureEntry#getLastBackgroundDuration(int)
+ * @see AttributedOpEntry#getLastBackgroundDuration(int)
*/
public long getLastBackgroundDuration(@OpFlags int flags) {
return getLastDuration(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
@@ -3922,7 +4078,7 @@
* @see #getLastDuration(int)
* @see #getLastForegroundDuration(int)
* @see #getLastBackgroundDuration(int)
- * @see OpFeatureEntry#getLastDuration(int, int, int)
+ * @see AttributedOpEntry#getLastDuration(int, int, int)
*/
public long getLastDuration(@UidState int fromUidState, @UidState int toUidState,
@OpFlags int flags) {
@@ -3997,7 +4153,7 @@
* @see #getLastForegroundProxyInfo(int)
* @see #getLastBackgroundProxyInfo(int)
* @see #getLastProxyInfo(int, int, int)
- * @see OpFeatureEntry#getLastProxyInfo(int)
+ * @see AttributedOpEntry#getLastProxyInfo(int)
*/
public @Nullable OpEventProxyInfo getLastProxyInfo(@OpFlags int flags) {
return getLastProxyInfo(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, flags);
@@ -4014,7 +4170,7 @@
* @see #getLastProxyInfo(int)
* @see #getLastBackgroundProxyInfo(int)
* @see #getLastProxyInfo(int, int, int)
- * @see OpFeatureEntry#getLastForegroundProxyInfo(int)
+ * @see AttributedOpEntry#getLastForegroundProxyInfo(int)
*/
public @Nullable OpEventProxyInfo getLastForegroundProxyInfo(@OpFlags int flags) {
return getLastProxyInfo(MAX_PRIORITY_UID_STATE, resolveFirstUnrestrictedUidState(mOp),
@@ -4032,7 +4188,7 @@
* @see #getLastProxyInfo(int)
* @see #getLastForegroundProxyInfo(int)
* @see #getLastProxyInfo(int, int, int)
- * @see OpFeatureEntry#getLastBackgroundProxyInfo(int)
+ * @see AttributedOpEntry#getLastBackgroundProxyInfo(int)
*/
public @Nullable OpEventProxyInfo getLastBackgroundProxyInfo(@OpFlags int flags) {
return getLastProxyInfo(resolveLastRestrictedUidState(mOp), MIN_PRIORITY_UID_STATE,
@@ -4052,7 +4208,7 @@
* @see #getLastProxyInfo(int)
* @see #getLastForegroundProxyInfo(int)
* @see #getLastBackgroundProxyInfo(int)
- * @see OpFeatureEntry#getLastProxyInfo(int, int, int)
+ * @see AttributedOpEntry#getLastProxyInfo(int, int, int)
*/
public @Nullable OpEventProxyInfo getLastProxyInfo(@UidState int fromUidState,
@UidState int toUidState, @OpFlags int flags) {
@@ -4086,15 +4242,15 @@
* The code of the op
* @param mode
* The mode of the op
- * @param features
- * The features that have been used when checking the op
+ * @param attributedOpEntries
+ * The attributions that have been used when noting the op
* @hide
*/
@DataClass.Generated.Member
public OpEntry(
@IntRange(from = 0, to = _NUM_OP - 1) int op,
@Mode int mode,
- @NonNull Map<String,OpFeatureEntry> features) {
+ @NonNull Map<String, AttributedOpEntry> attributedOpEntries) {
this.mOp = op;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mOp,
@@ -4103,9 +4259,9 @@
this.mMode = mode;
com.android.internal.util.AnnotationValidations.validate(
Mode.class, null, mMode);
- this.mFeatures = features;
+ this.mAttributedOpEntries = attributedOpEntries;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mFeatures);
+ NonNull.class, null, mAttributedOpEntries);
// onConstructed(); // You can define this method to get a callback
}
@@ -4119,14 +4275,14 @@
}
/**
- * The features that have been used when checking the op keyed by id of the feature.
+ * The attributed entries keyed by attribution tag.
*
- * @see Context#createFeatureContext(String)
+ * @see Context#createAttributionContext(String)
* @see #noteOp(String, int, String, String, String)
*/
@DataClass.Generated.Member
- public @NonNull Map<String,OpFeatureEntry> getFeatures() {
- return mFeatures;
+ public @NonNull Map<String, AttributedOpEntry> getAttributedOpEntries() {
+ return mAttributedOpEntries;
}
@Override
@@ -4137,7 +4293,7 @@
dest.writeInt(mOp);
dest.writeInt(mMode);
- dest.writeMap(mFeatures);
+ dest.writeMap(mAttributedOpEntries);
}
@Override
@@ -4153,8 +4309,8 @@
int op = in.readInt();
int mode = in.readInt();
- Map<String,OpFeatureEntry> features = new java.util.LinkedHashMap<>();
- in.readMap(features, OpFeatureEntry.class.getClassLoader());
+ Map<String, AttributedOpEntry> attributions = new java.util.LinkedHashMap<>();
+ in.readMap(attributions, AttributedOpEntry.class.getClassLoader());
this.mOp = op;
com.android.internal.util.AnnotationValidations.validate(
@@ -4164,9 +4320,9 @@
this.mMode = mode;
com.android.internal.util.AnnotationValidations.validate(
Mode.class, null, mMode);
- this.mFeatures = features;
+ this.mAttributedOpEntries = attributions;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mFeatures);
+ NonNull.class, null, mAttributedOpEntries);
// onConstructed(); // You can define this method to get a callback
}
@@ -4190,7 +4346,7 @@
time = 1574809856259L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final @android.app.Mode int mMode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.app.OpFeatureEntry> mFeatures\npublic @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getOpStr()}\") int getOp()\npublic @android.annotation.NonNull java.lang.String getOpStr()\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getAccessTime(int, int)}\") long getTime()\npublic @java.lang.Deprecated long getLastAccessTime(int)\npublic @java.lang.Deprecated long getLastAccessForegroundTime(int)\npublic @java.lang.Deprecated long getLastAccessBackgroundTime(int)\npublic @java.lang.Deprecated long getLastAccessTime(int,int,int)\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getLastRejectTime(int, int, int)}\") long getRejectTime()\npublic @java.lang.Deprecated long getLastRejectTime(int)\npublic @java.lang.Deprecated long getLastRejectForegroundTime(int)\npublic @java.lang.Deprecated long getLastRejectBackgroundTime(int)\npublic @java.lang.Deprecated long getLastRejectTime(int,int,int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic boolean isRunning()\nprivate android.app.NoteOpEvent getLastAccessEvent(int,int,int)\npublic @java.lang.Deprecated long getDuration()\npublic @java.lang.Deprecated long getLastForegroundDuration(int)\npublic @java.lang.Deprecated long getLastBackgroundDuration(int)\npublic @java.lang.Deprecated long getLastDuration(int,int,int)\npublic @java.lang.Deprecated int getProxyUid()\npublic @java.lang.Deprecated @android.annotation.Nullable java.lang.String getProxyPackageName()\nprivate @android.app.UidState int getLastAccessUidStateForFlagsInStatesOfAllFeatures(int,int,int)\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\nprivate @android.app.UidState int getLastRejectUidStateForFlagsInStatesOfAllFeatures(int,int,int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\nprivate int getProxyUid(int,int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\nprivate @android.annotation.Nullable java.lang.String getProxyPackageName(int,int,int)\nclass OpEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.IntRange(from=0L, to=_NUM_OP - 1) int mOp\nprivate final @android.app.Mode int mMode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.app.OpAttributionEntry> mAttributions\npublic @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getOpStr()}\") int getOp()\npublic @android.annotation.NonNull java.lang.String getOpStr()\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getAccessTime(int, int)}\") long getTime()\npublic @java.lang.Deprecated long getLastAccessTime(int)\npublic @java.lang.Deprecated long getLastAccessForegroundTime(int)\npublic @java.lang.Deprecated long getLastAccessBackgroundTime(int)\npublic @java.lang.Deprecated long getLastAccessTime(int,int,int)\npublic @java.lang.Deprecated @android.annotation.UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.Q, publicAlternatives=\"{@code \" + \"#getLastRejectTime(int, int, int)}\") long getRejectTime()\npublic @java.lang.Deprecated long getLastRejectTime(int)\npublic @java.lang.Deprecated long getLastRejectForegroundTime(int)\npublic @java.lang.Deprecated long getLastRejectBackgroundTime(int)\npublic @java.lang.Deprecated long getLastRejectTime(int,int,int)\npublic long getAccessTime(int,int)\npublic long getRejectTime(int,int)\npublic boolean isRunning()\nprivate android.app.NoteOpEvent getLastAccessEvent(int,int,int)\npublic @java.lang.Deprecated long getDuration()\npublic @java.lang.Deprecated long getLastForegroundDuration(int)\npublic @java.lang.Deprecated long getLastBackgroundDuration(int)\npublic @java.lang.Deprecated long getLastDuration(int,int,int)\npublic @java.lang.Deprecated int getProxyUid()\npublic @java.lang.Deprecated @android.annotation.Nullable java.lang.String getProxyPackageName()\nprivate @android.app.UidState int getLastAccessUidStateForFlagsInStatesOfAllAttributions(int,int,int)\npublic @android.app.UidState int getLastAccessUidState(int)\npublic @android.app.UidState int getLastForegroundAccessUidState(int)\npublic @android.app.UidState int getLastBackgroundAccessUidState(int)\nprivate @android.app.UidState int getLastRejectUidStateForFlagsInStatesOfAllAttributions(int,int,int)\npublic @android.app.UidState int getLastRejectUidState(int)\npublic @android.app.UidState int getLastForegroundRejectUidState(int)\npublic @android.app.UidState int getLastBackgroundRejectUidState(int)\npublic long getDuration(int,int)\npublic int getProxyUid(int,int)\nprivate int getProxyUid(int,int,int)\npublic @android.annotation.Nullable java.lang.String getProxyPackageName(int,int)\nprivate @android.annotation.Nullable java.lang.String getProxyPackageName(int,int,int)\nclass OpEntry extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
*/
@@ -4206,7 +4362,7 @@
void visitHistoricalOps(@NonNull HistoricalOps ops);
void visitHistoricalUidOps(@NonNull HistoricalUidOps ops);
void visitHistoricalPackageOps(@NonNull HistoricalPackageOps ops);
- void visitHistoricalFeatureOps(@NonNull HistoricalFeatureOps ops);
+ void visitHistoricalAttributionOps(@NonNull AttributedHistoricalOps ops);
void visitHistoricalOp(@NonNull HistoricalOp ops);
}
@@ -4219,7 +4375,7 @@
@IntDef(flag = true, prefix = { "FILTER_BY_" }, value = {
FILTER_BY_UID,
FILTER_BY_PACKAGE_NAME,
- FILTER_BY_FEATURE_ID,
+ FILTER_BY_ATTRIBUTION_TAG,
FILTER_BY_OP_NAMES
})
public @interface HistoricalOpsRequestFilter {}
@@ -4239,11 +4395,11 @@
public static final int FILTER_BY_PACKAGE_NAME = 1<<1;
/**
- * Filter historical appop request by feature id.
+ * Filter historical appop request by attribution tag.
*
* @hide
*/
- public static final int FILTER_BY_FEATURE_ID = 1<<2;
+ public static final int FILTER_BY_ATTRIBUTION_TAG = 1<<2;
/**
* Filter historical appop request by op names.
@@ -4264,7 +4420,7 @@
public static final class HistoricalOpsRequest {
private final int mUid;
private final @Nullable String mPackageName;
- private final @Nullable String mFeatureId;
+ private final @Nullable String mAttributionTag;
private final @Nullable List<String> mOpNames;
private final @HistoricalOpsRequestFilter int mFilter;
private final long mBeginTimeMillis;
@@ -4272,12 +4428,12 @@
private final @OpFlags int mFlags;
private HistoricalOpsRequest(int uid, @Nullable String packageName,
- @Nullable String featureId, @Nullable List<String> opNames,
+ @Nullable String attributionTag, @Nullable List<String> opNames,
@HistoricalOpsRequestFilter int filter, long beginTimeMillis,
long endTimeMillis, @OpFlags int flags) {
mUid = uid;
mPackageName = packageName;
- mFeatureId = featureId;
+ mAttributionTag = attributionTag;
mOpNames = opNames;
mFilter = filter;
mBeginTimeMillis = beginTimeMillis;
@@ -4295,7 +4451,7 @@
public static final class Builder {
private int mUid = Process.INVALID_UID;
private @Nullable String mPackageName;
- private @Nullable String mFeatureId;
+ private @Nullable String mAttributionTag;
private @Nullable List<String> mOpNames;
private @HistoricalOpsRequestFilter int mFilter;
private final long mBeginTimeMillis;
@@ -4359,14 +4515,14 @@
}
/**
- * Sets the feature id to query for.
+ * Sets the attribution tag to query for.
*
- * @param featureId The id of the feature.
+ * @param attributionTag attribution tag
* @return This builder.
*/
- public @NonNull Builder setFeatureId(@Nullable String featureId) {
- mFeatureId = featureId;
- mFilter |= FILTER_BY_FEATURE_ID;
+ public @NonNull Builder setAttributionTag(@Nullable String attributionTag) {
+ mAttributionTag = attributionTag;
+ mFilter |= FILTER_BY_ATTRIBUTION_TAG;
return this;
}
@@ -4417,7 +4573,7 @@
* @return a new {@link HistoricalOpsRequest}.
*/
public @NonNull HistoricalOpsRequest build() {
- return new HistoricalOpsRequest(mUid, mPackageName, mFeatureId, mOpNames,
+ return new HistoricalOpsRequest(mUid, mPackageName, mAttributionTag, mOpNames,
mFilter, mBeginTimeMillis, mEndTimeMillis, mFlags);
}
}
@@ -4577,7 +4733,7 @@
*
* @param uid Uid to filter for.
* @param packageName Package to filter for.
- * @param featureId Package to filter for.
+ * @param attributionTag attribution tag to filter for
* @param opNames Ops to filter for.
* @param filter Which parameters to filter on.
* @param beginTimeMillis The begin time to filter for or {@link Long#MIN_VALUE} for all.
@@ -4585,7 +4741,7 @@
*
* @hide
*/
- public void filter(int uid, @Nullable String packageName, @Nullable String featureId,
+ public void filter(int uid, @Nullable String packageName, @Nullable String attributionTag,
@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
long beginTimeMillis, long endTimeMillis) {
final long durationMillis = getDurationMillis();
@@ -4599,7 +4755,7 @@
if ((filter & FILTER_BY_UID) != 0 && uid != uidOp.getUid()) {
mHistoricalUidOps.removeAt(i);
} else {
- uidOp.filter(packageName, featureId, opNames, filter, scaleFactor);
+ uidOp.filter(packageName, attributionTag, opNames, filter, scaleFactor);
if (uidOp.getPackageCount() == 0) {
mHistoricalUidOps.removeAt(i);
}
@@ -4630,28 +4786,28 @@
/** @hide */
@TestApi
public void increaseAccessCount(int opCode, int uid, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
long increment) {
getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode,
- packageName, featureId, uidState, flags, increment);
+ packageName, attributionTag, uidState, flags, increment);
}
/** @hide */
@TestApi
public void increaseRejectCount(int opCode, int uid, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
long increment) {
getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode,
- packageName, featureId, uidState, flags, increment);
+ packageName, attributionTag, uidState, flags, increment);
}
/** @hide */
@TestApi
public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
long increment) {
getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode,
- packageName, featureId, uidState, flags, increment);
+ packageName, attributionTag, uidState, flags, increment);
}
/** @hide */
@@ -4931,7 +5087,7 @@
}
}
- private void filter(@Nullable String packageName, @Nullable String featureId,
+ private void filter(@Nullable String packageName, @Nullable String attributionTag,
@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
double fractionToRemove) {
final int packageCount = getPackageCount();
@@ -4941,8 +5097,8 @@
packageOps.getPackageName())) {
mHistoricalPackageOps.removeAt(i);
} else {
- packageOps.filter(featureId, opNames, filter, fractionToRemove);
- if (packageOps.getFeatureCount() == 0) {
+ packageOps.filter(attributionTag, opNames, filter, fractionToRemove);
+ if (packageOps.getAttributedOpsCount() == 0) {
mHistoricalPackageOps.removeAt(i);
}
}
@@ -4961,24 +5117,24 @@
}
private void increaseAccessCount(int opCode, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
long increment) {
getOrCreateHistoricalPackageOps(packageName).increaseAccessCount(
- opCode, featureId, uidState, flags, increment);
+ opCode, attributionTag, uidState, flags, increment);
}
private void increaseRejectCount(int opCode, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
long increment) {
getOrCreateHistoricalPackageOps(packageName).increaseRejectCount(
- opCode, featureId, uidState, flags, increment);
+ opCode, attributionTag, uidState, flags, increment);
}
private void increaseAccessDuration(int opCode, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
long increment) {
getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration(
- opCode, featureId, uidState, flags, increment);
+ opCode, attributionTag, uidState, flags, increment);
}
/**
@@ -5123,7 +5279,7 @@
@SystemApi
public static final class HistoricalPackageOps implements Parcelable {
private final @NonNull String mPackageName;
- private @Nullable ArrayMap<String, HistoricalFeatureOps> mHistoricalFeatureOps;
+ private @Nullable ArrayMap<String, AttributedHistoricalOps> mAttributedHistoricalOps;
/** @hide */
public HistoricalPackageOps(@NonNull String packageName) {
@@ -5132,70 +5288,71 @@
private HistoricalPackageOps(@NonNull HistoricalPackageOps other) {
mPackageName = other.mPackageName;
- final int opCount = other.getFeatureCount();
+ final int opCount = other.getAttributedOpsCount();
for (int i = 0; i < opCount; i++) {
- final HistoricalFeatureOps origOps = other.getFeatureOpsAt(i);
- final HistoricalFeatureOps cloneOps = new HistoricalFeatureOps(origOps);
- if (mHistoricalFeatureOps == null) {
- mHistoricalFeatureOps = new ArrayMap<>(opCount);
+ final AttributedHistoricalOps origOps = other.getAttributedOpsAt(i);
+ final AttributedHistoricalOps cloneOps = new AttributedHistoricalOps(origOps);
+ if (mAttributedHistoricalOps == null) {
+ mAttributedHistoricalOps = new ArrayMap<>(opCount);
}
- mHistoricalFeatureOps.put(cloneOps.getFeatureId(), cloneOps);
+ mAttributedHistoricalOps.put(cloneOps.getTag(), cloneOps);
}
}
private HistoricalPackageOps(@NonNull Parcel parcel) {
mPackageName = parcel.readString();
- mHistoricalFeatureOps = parcel.createTypedArrayMap(HistoricalFeatureOps.CREATOR);
+ mAttributedHistoricalOps = parcel.createTypedArrayMap(AttributedHistoricalOps.CREATOR);
}
private @Nullable HistoricalPackageOps splice(double fractionToRemove) {
HistoricalPackageOps splice = null;
- final int featureCount = getFeatureCount();
- for (int i = 0; i < featureCount; i++) {
- final HistoricalFeatureOps origOps = getFeatureOpsAt(i);
- final HistoricalFeatureOps spliceOps = origOps.splice(fractionToRemove);
+ final int attributionCount = getAttributedOpsCount();
+ for (int i = 0; i < attributionCount; i++) {
+ final AttributedHistoricalOps origOps = getAttributedOpsAt(i);
+ final AttributedHistoricalOps spliceOps = origOps.splice(fractionToRemove);
if (spliceOps != null) {
if (splice == null) {
splice = new HistoricalPackageOps(mPackageName);
}
- if (splice.mHistoricalFeatureOps == null) {
- splice.mHistoricalFeatureOps = new ArrayMap<>();
+ if (splice.mAttributedHistoricalOps == null) {
+ splice.mAttributedHistoricalOps = new ArrayMap<>();
}
- splice.mHistoricalFeatureOps.put(spliceOps.getFeatureId(), spliceOps);
+ splice.mAttributedHistoricalOps.put(spliceOps.getTag(), spliceOps);
}
}
return splice;
}
private void merge(@NonNull HistoricalPackageOps other) {
- final int featureCount = other.getFeatureCount();
- for (int i = 0; i < featureCount; i++) {
- final HistoricalFeatureOps otherFeatureOps = other.getFeatureOpsAt(i);
- final HistoricalFeatureOps thisFeatureOps = getFeatureOps(
- otherFeatureOps.getFeatureId());
- if (thisFeatureOps != null) {
- thisFeatureOps.merge(otherFeatureOps);
+ final int attributionCount = other.getAttributedOpsCount();
+ for (int i = 0; i < attributionCount; i++) {
+ final AttributedHistoricalOps otherAttributionOps = other.getAttributedOpsAt(i);
+ final AttributedHistoricalOps thisAttributionOps = getAttributedOps(
+ otherAttributionOps.getTag());
+ if (thisAttributionOps != null) {
+ thisAttributionOps.merge(otherAttributionOps);
} else {
- if (mHistoricalFeatureOps == null) {
- mHistoricalFeatureOps = new ArrayMap<>();
+ if (mAttributedHistoricalOps == null) {
+ mAttributedHistoricalOps = new ArrayMap<>();
}
- mHistoricalFeatureOps.put(otherFeatureOps.getFeatureId(), otherFeatureOps);
+ mAttributedHistoricalOps.put(otherAttributionOps.getTag(),
+ otherAttributionOps);
}
}
}
- private void filter(@Nullable String featureId, @Nullable String[] opNames,
+ private void filter(@Nullable String attributionTag, @Nullable String[] opNames,
@HistoricalOpsRequestFilter int filter, double fractionToRemove) {
- final int featureCount = getFeatureCount();
- for (int i = featureCount - 1; i >= 0; i--) {
- final HistoricalFeatureOps featureOps = getFeatureOpsAt(i);
- if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(featureId,
- featureOps.getFeatureId())) {
- mHistoricalFeatureOps.removeAt(i);
+ final int attributionCount = getAttributedOpsCount();
+ for (int i = attributionCount - 1; i >= 0; i--) {
+ final AttributedHistoricalOps attributionOps = getAttributedOpsAt(i);
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(attributionTag,
+ attributionOps.getTag())) {
+ mAttributedHistoricalOps.removeAt(i);
} else {
- featureOps.filter(opNames, filter, fractionToRemove);
- if (featureOps.getOpCount() == 0) {
- mHistoricalFeatureOps.removeAt(i);
+ attributionOps.filter(opNames, filter, fractionToRemove);
+ if (attributionOps.getOpCount() == 0) {
+ mAttributedHistoricalOps.removeAt(i);
}
}
}
@@ -5203,38 +5360,38 @@
private void accept(@NonNull HistoricalOpsVisitor visitor) {
visitor.visitHistoricalPackageOps(this);
- final int featureCount = getFeatureCount();
- for (int i = 0; i < featureCount; i++) {
- getFeatureOpsAt(i).accept(visitor);
+ final int attributionCount = getAttributedOpsCount();
+ for (int i = 0; i < attributionCount; i++) {
+ getAttributedOpsAt(i).accept(visitor);
}
}
private boolean isEmpty() {
- final int featureCount = getFeatureCount();
- for (int i = featureCount - 1; i >= 0; i--) {
- final HistoricalFeatureOps featureOps = mHistoricalFeatureOps.valueAt(i);
- if (!featureOps.isEmpty()) {
+ final int attributionCount = getAttributedOpsCount();
+ for (int i = attributionCount - 1; i >= 0; i--) {
+ final AttributedHistoricalOps attributionOps = mAttributedHistoricalOps.valueAt(i);
+ if (!attributionOps.isEmpty()) {
return false;
}
}
return true;
}
- private void increaseAccessCount(int opCode, @Nullable String featureId,
+ private void increaseAccessCount(int opCode, @Nullable String attributionTag,
@UidState int uidState, @OpFlags int flags, long increment) {
- getOrCreateHistoricalFeatureOps(featureId).increaseAccessCount(
+ getOrCreateAttributedHistoricalOps(attributionTag).increaseAccessCount(
opCode, uidState, flags, increment);
}
- private void increaseRejectCount(int opCode, @Nullable String featureId,
+ private void increaseRejectCount(int opCode, @Nullable String attributionTag,
@UidState int uidState, @OpFlags int flags, long increment) {
- getOrCreateHistoricalFeatureOps(featureId).increaseRejectCount(
+ getOrCreateAttributedHistoricalOps(attributionTag).increaseRejectCount(
opCode, uidState, flags, increment);
}
- private void increaseAccessDuration(int opCode, @Nullable String featureId,
+ private void increaseAccessDuration(int opCode, @Nullable String attributionTag,
@UidState int uidState, @OpFlags int flags, long increment) {
- getOrCreateHistoricalFeatureOps(featureId).increaseAccessDuration(
+ getOrCreateAttributedHistoricalOps(attributionTag).increaseAccessDuration(
opCode, uidState, flags, increment);
}
@@ -5247,17 +5404,18 @@
return mPackageName;
}
- private @NonNull HistoricalFeatureOps getOrCreateHistoricalFeatureOps(
- @Nullable String featureId) {
- if (mHistoricalFeatureOps == null) {
- mHistoricalFeatureOps = new ArrayMap<>();
+ private @NonNull AttributedHistoricalOps getOrCreateAttributedHistoricalOps(
+ @Nullable String attributionTag) {
+ if (mAttributedHistoricalOps == null) {
+ mAttributedHistoricalOps = new ArrayMap<>();
}
- HistoricalFeatureOps historicalFeatureOp = mHistoricalFeatureOps.get(featureId);
- if (historicalFeatureOp == null) {
- historicalFeatureOp = new HistoricalFeatureOps(featureId);
- mHistoricalFeatureOps.put(featureId, historicalFeatureOp);
+ AttributedHistoricalOps historicalAttributionOp = mAttributedHistoricalOps.get(
+ attributionTag);
+ if (historicalAttributionOp == null) {
+ historicalAttributionOp = new AttributedHistoricalOps(attributionTag);
+ mAttributedHistoricalOps.put(attributionTag, historicalAttributionOp);
}
- return historicalFeatureOp;
+ return historicalAttributionOp;
}
/**
@@ -5268,13 +5426,13 @@
*/
public @IntRange(from = 0) int getOpCount() {
int numOps = 0;
- int numFeatures = getFeatureCount();
+ int numAttributions = getAttributedOpsCount();
for (int code = 0; code < _NUM_OP; code++) {
String opName = opToPublicName(code);
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- if (getFeatureOpsAt(featureNum).getOp(opName) != null) {
+ for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+ if (getAttributedOpsAt(attributionNum).getOp(opName) != null) {
numOps++;
break;
}
@@ -5287,7 +5445,7 @@
/**
* Gets the historical op at a given index.
*
- * <p>This combines the counts from all features.
+ * <p>This combines the counts from all attributions.
*
* @param index The index to lookup.
* @return The op at the given index.
@@ -5295,13 +5453,13 @@
*/
public @NonNull HistoricalOp getOpAt(@IntRange(from = 0) int index) {
int numOpsFound = 0;
- int numFeatures = getFeatureCount();
+ int numAttributions = getAttributedOpsCount();
for (int code = 0; code < _NUM_OP; code++) {
String opName = opToPublicName(code);
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- if (getFeatureOpsAt(featureNum).getOp(opName) != null) {
+ for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+ if (getAttributedOpsAt(attributionNum).getOp(opName) != null) {
if (numOpsFound == index) {
return getOp(opName);
} else {
@@ -5318,25 +5476,25 @@
/**
* Gets the historical entry for a given op name.
*
- * <p>This combines the counts from all features.
+ * <p>This combines the counts from all attributions.
*
* @param opName The op name.
* @return The historical entry for that op name.
*/
public @Nullable HistoricalOp getOp(@NonNull String opName) {
- if (mHistoricalFeatureOps == null) {
+ if (mAttributedHistoricalOps == null) {
return null;
}
HistoricalOp combinedOp = null;
- int numFeatures = getFeatureCount();
- for (int i = 0; i < numFeatures; i++) {
- HistoricalOp featureOp = getFeatureOpsAt(i).getOp(opName);
- if (featureOp != null) {
+ int numAttributions = getAttributedOpsCount();
+ for (int i = 0; i < numAttributions; i++) {
+ HistoricalOp attributionOp = getAttributedOpsAt(i).getOp(opName);
+ if (attributionOp != null) {
if (combinedOp == null) {
- combinedOp = new HistoricalOp(featureOp);
+ combinedOp = new HistoricalOp(attributionOp);
} else {
- combinedOp.merge(featureOp);
+ combinedOp.merge(attributionOp);
}
}
}
@@ -5352,7 +5510,7 @@
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeString(mPackageName);
- parcel.writeTypedArrayMap(mHistoricalFeatureOps, flags);
+ parcel.writeTypedArrayMap(mAttributedHistoricalOps, flags);
}
public static final @android.annotation.NonNull Creator<HistoricalPackageOps> CREATOR =
@@ -5380,11 +5538,11 @@
if (!mPackageName.equals(other.mPackageName)) {
return false;
}
- if (mHistoricalFeatureOps == null) {
- if (other.mHistoricalFeatureOps != null) {
+ if (mAttributedHistoricalOps == null) {
+ if (other.mAttributedHistoricalOps != null) {
return false;
}
- } else if (!mHistoricalFeatureOps.equals(other.mHistoricalFeatureOps)) {
+ } else if (!mAttributedHistoricalOps.equals(other.mAttributedHistoricalOps)) {
return false;
}
return true;
@@ -5393,58 +5551,58 @@
@Override
public int hashCode() {
int result = mPackageName != null ? mPackageName.hashCode() : 0;
- result = 31 * result + (mHistoricalFeatureOps != null ? mHistoricalFeatureOps.hashCode()
- : 0);
+ result = 31 * result + (mAttributedHistoricalOps != null
+ ? mAttributedHistoricalOps.hashCode() : 0);
return result;
}
/**
- * Gets number of feature with historical ops.
+ * Gets number of attributed historical ops.
*
- * @return The number of feature with historical ops.
+ * @return The number of attribution with historical ops.
*
- * @see #getFeatureOpsAt(int)
+ * @see #getAttributedOpsAt(int)
*/
- public @IntRange(from = 0) int getFeatureCount() {
- if (mHistoricalFeatureOps == null) {
+ public @IntRange(from = 0) int getAttributedOpsCount() {
+ if (mAttributedHistoricalOps == null) {
return 0;
}
- return mHistoricalFeatureOps.size();
+ return mAttributedHistoricalOps.size();
}
/**
- * Gets the historical feature ops at a given index.
+ * Gets the attributed historical ops at a given index.
*
* @param index The index.
*
- * @return The historical feature ops at the given index.
+ * @return The historical attribution ops at the given index.
*
- * @see #getFeatureCount()
+ * @see #getAttributedOpsCount()
*/
- public @NonNull HistoricalFeatureOps getFeatureOpsAt(@IntRange(from = 0) int index) {
- if (mHistoricalFeatureOps == null) {
+ public @NonNull AttributedHistoricalOps getAttributedOpsAt(@IntRange(from = 0) int index) {
+ if (mAttributedHistoricalOps == null) {
throw new IndexOutOfBoundsException();
}
- return mHistoricalFeatureOps.valueAt(index);
+ return mAttributedHistoricalOps.valueAt(index);
}
/**
- * Gets the historical feature ops for a given feature.
+ * Gets the attributed historical ops for a given attribution tag.
*
- * @param featureId The feature id.
+ * @param attributionTag The attribution tag.
*
- * @return The historical ops for the feature.
+ * @return The historical ops for the attribution.
*/
- public @Nullable HistoricalFeatureOps getFeatureOps(@NonNull String featureId) {
- if (mHistoricalFeatureOps == null) {
+ public @Nullable AttributedHistoricalOps getAttributedOps(@NonNull String attributionTag) {
+ if (mAttributedHistoricalOps == null) {
return null;
}
- return mHistoricalFeatureOps.get(featureId);
+ return mAttributedHistoricalOps.get(attributionTag);
}
}
/**
- * This class represents historical app op information about a feature in a package.
+ * This class represents historical app op information about a attribution in a package.
*
* @hide
*/
@@ -5454,20 +5612,20 @@
@DataClass(genHiddenConstructor = true,
genEqualsHashCode = true, genHiddenCopyConstructor = true) */
@DataClass.Suppress("getHistoricalOps")
- public static final class HistoricalFeatureOps implements Parcelable {
- /** Id of the {@link Context#createFeatureContext feature} in the package */
- private final @Nullable String mFeatureId;
+ public static final class AttributedHistoricalOps implements Parcelable {
+ /** {@link Context#createAttributionContext attribution} tag */
+ private final @Nullable String mTag;
- /** Ops for this feature */
+ /** Ops for this attribution */
private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps;
/** @hide */
- public HistoricalFeatureOps(@NonNull String featureId) {
- mFeatureId = featureId;
+ public AttributedHistoricalOps(@NonNull String tag) {
+ mTag = tag;
}
- private HistoricalFeatureOps(@NonNull HistoricalFeatureOps other) {
- mFeatureId = other.mFeatureId;
+ private AttributedHistoricalOps(@NonNull AttributedHistoricalOps other) {
+ mTag = other.mTag;
final int opCount = other.getOpCount();
for (int i = 0; i < opCount; i++) {
final HistoricalOp origOp = other.getOpAt(i);
@@ -5479,15 +5637,15 @@
}
}
- private @Nullable HistoricalFeatureOps splice(double fractionToRemove) {
- HistoricalFeatureOps splice = null;
+ private @Nullable AttributedHistoricalOps splice(double fractionToRemove) {
+ AttributedHistoricalOps splice = null;
final int opCount = getOpCount();
for (int i = 0; i < opCount; i++) {
final HistoricalOp origOps = getOpAt(i);
final HistoricalOp spliceOps = origOps.splice(fractionToRemove);
if (spliceOps != null) {
if (splice == null) {
- splice = new HistoricalFeatureOps(mFeatureId, null);
+ splice = new AttributedHistoricalOps(mTag, null);
}
if (splice.mHistoricalOps == null) {
splice.mHistoricalOps = new ArrayMap<>();
@@ -5498,7 +5656,7 @@
return splice;
}
- private void merge(@NonNull HistoricalFeatureOps other) {
+ private void merge(@NonNull AttributedHistoricalOps other) {
final int opCount = other.getOpCount();
for (int i = 0; i < opCount; i++) {
final HistoricalOp otherOp = other.getOpAt(i);
@@ -5595,7 +5753,7 @@
}
private void accept(@NonNull HistoricalOpsVisitor visitor) {
- visitor.visitHistoricalFeatureOps(this);
+ visitor.visitHistoricalAttributionOps(this);
final int opCount = getOpCount();
for (int i = 0; i < opCount; i++) {
getOpAt(i).accept(visitor);
@@ -5631,46 +5789,46 @@
/**
- * Creates a new HistoricalFeatureOps.
+ * Creates a new HistoricalAttributionOps.
*
- * @param featureId
- * Id of the {@link Context#createFeatureContext feature} in the package
+ * @param tag
+ * {@link Context#createAttributionContext attribution} tag
* @param historicalOps
- * Ops for this feature
+ * Ops for this attribution
* @hide
*/
@DataClass.Generated.Member
- public HistoricalFeatureOps(
- @Nullable String featureId,
+ public AttributedHistoricalOps(
+ @Nullable String tag,
@Nullable ArrayMap<String,HistoricalOp> historicalOps) {
- this.mFeatureId = featureId;
+ this.mTag = tag;
this.mHistoricalOps = historicalOps;
// onConstructed(); // You can define this method to get a callback
}
/**
- * Id of the {@link Context#createFeatureContext feature} in the package
+ * {@link Context#createAttributionContext attribution} tag
*/
@DataClass.Generated.Member
- public @Nullable String getFeatureId() {
- return mFeatureId;
+ public @Nullable String getTag() {
+ return mTag;
}
@Override
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(HistoricalFeatureOps other) { ... }
+ // boolean fieldNameEquals(HistoricalAttributionOps other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
- HistoricalFeatureOps that = (HistoricalFeatureOps) o;
+ AttributedHistoricalOps that = (AttributedHistoricalOps) o;
//noinspection PointlessBooleanExpression
return true
- && Objects.equals(mFeatureId, that.mFeatureId)
+ && Objects.equals(mTag, that.mTag)
&& Objects.equals(mHistoricalOps, that.mHistoricalOps);
}
@@ -5681,7 +5839,7 @@
// int fieldNameHashCode() { ... }
int _hash = 1;
- _hash = 31 * _hash + Objects.hashCode(mFeatureId);
+ _hash = 31 * _hash + Objects.hashCode(mTag);
_hash = 31 * _hash + Objects.hashCode(mHistoricalOps);
return _hash;
}
@@ -5693,10 +5851,10 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mFeatureId != null) flg |= 0x1;
+ if (mTag != null) flg |= 0x1;
if (mHistoricalOps != null) flg |= 0x2;
dest.writeByte(flg);
- if (mFeatureId != null) dest.writeString(mFeatureId);
+ if (mTag != null) dest.writeString(mTag);
if (mHistoricalOps != null) dest.writeMap(mHistoricalOps);
}
@@ -5707,35 +5865,35 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ HistoricalFeatureOps(@NonNull Parcel in) {
+ /* package-private */ AttributedHistoricalOps(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
- String featureId = (flg & 0x1) == 0 ? null : in.readString();
+ String attributionTag = (flg & 0x1) == 0 ? null : in.readString();
ArrayMap<String,HistoricalOp> historicalOps = null;
if ((flg & 0x2) != 0) {
historicalOps = new ArrayMap();
in.readMap(historicalOps, HistoricalOp.class.getClassLoader());
}
- this.mFeatureId = featureId;
+ this.mTag = attributionTag;
this.mHistoricalOps = historicalOps;
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<HistoricalFeatureOps> CREATOR
- = new Parcelable.Creator<HistoricalFeatureOps>() {
+ public static final @NonNull Parcelable.Creator<AttributedHistoricalOps> CREATOR
+ = new Parcelable.Creator<AttributedHistoricalOps>() {
@Override
- public HistoricalFeatureOps[] newArray(int size) {
- return new HistoricalFeatureOps[size];
+ public AttributedHistoricalOps[] newArray(int size) {
+ return new AttributedHistoricalOps[size];
}
@Override
- public HistoricalFeatureOps createFromParcel(@NonNull Parcel in) {
- return new HistoricalFeatureOps(in);
+ public AttributedHistoricalOps createFromParcel(@NonNull Parcel in) {
+ return new AttributedHistoricalOps(in);
}
};
@@ -5744,7 +5902,7 @@
time = 1578113234821L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
- inputSignatures = "private final @android.annotation.Nullable java.lang.String mFeatureId\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalFeatureOps splice(double)\nprivate void merge(android.app.HistoricalFeatureOps)\nprivate void filter(java.lang.String[],int,double)\nprivate boolean isEmpty()\nprivate void increaseAccessCount(int,int,int,long)\nprivate void increaseRejectCount(int,int,int,long)\nprivate void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalFeatureOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)")
+ inputSignatures = "private final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalAttributionOps splice(double)\nprivate void merge(android.app.HistoricalAttributionOps)\nprivate void filter(java.lang.String[],int,double)\nprivate boolean isEmpty()\nprivate void increaseAccessCount(int,int,int,long)\nprivate void increaseRejectCount(int,int,int,long)\nprivate void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalAttributionOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)")
@Deprecated
private void __metadata() {}
*/
@@ -5878,6 +6036,11 @@
return mOp;
}
+ /** @hide */
+ public int getLoggingOpCode() {
+ return AppOpsManager.opToLoggingId(mOp);
+ }
+
/**
* Gets the number times the op was accessed (performed) in the foreground.
*
@@ -6420,7 +6583,7 @@
Objects.requireNonNull(executor, "executor cannot be null");
Objects.requireNonNull(callback, "callback cannot be null");
try {
- mService.getHistoricalOps(request.mUid, request.mPackageName, request.mFeatureId,
+ mService.getHistoricalOps(request.mUid, request.mPackageName, request.mAttributionTag,
request.mOpNames, request.mFilter, request.mBeginTimeMillis,
request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
@@ -6460,8 +6623,9 @@
Objects.requireNonNull(callback, "callback cannot be null");
try {
mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
- request.mFeatureId, request.mOpNames, request.mFilter, request.mBeginTimeMillis,
- request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
+ request.mAttributionTag, request.mOpNames, request.mFilter,
+ request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags,
+ new RemoteCallback((result) -> {
final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
final long identity = Binder.clearCallingIdentity();
try {
@@ -6712,6 +6876,13 @@
};
mModeWatchers.put(callback, cb);
}
+
+ // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
+ if (!Compatibility.isChangeEnabled(
+ CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) {
+ flags |= CALL_BACK_ON_SWITCHED_OP;
+ }
+
try {
mService.startWatchingModeWithFlags(op, packageName, flags, cb);
} catch (RemoteException e) {
@@ -7044,8 +7215,8 @@
* @param op The operation to note. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
- * null} for default feature
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
+ * null} for default attribution
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7055,8 +7226,8 @@
* @throws SecurityException If the app has been configured to crash on this op.
*/
public int noteOp(@NonNull String op, int uid, @Nullable String packageName,
- @Nullable String featureId, @Nullable String message) {
- return noteOp(strOpToOp(op), uid, packageName, featureId, message);
+ @Nullable String attributionTag, @Nullable String message) {
+ return noteOp(strOpToOp(op), uid, packageName, attributionTag, message);
}
/**
@@ -7072,7 +7243,8 @@
* @param op The operation to note. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The feature in the app or {@code null} for default feature
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
+ * null} for default attribution
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7083,9 +7255,9 @@
*
* @hide
*/
- public int noteOp(int op, int uid, @Nullable String packageName, @Nullable String featureId,
- @Nullable String message) {
- final int mode = noteOpNoThrow(op, uid, packageName, featureId, message);
+ public int noteOp(int op, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, @Nullable String message) {
+ final int mode = noteOpNoThrow(op, uid, packageName, attributionTag, message);
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
@@ -7120,8 +7292,8 @@
* @param op The operation to note. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
- * null} for default feature
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
+ * null} for default attribution
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7129,8 +7301,8 @@
* causing the app to crash).
*/
public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
- @Nullable String featureId, @Nullable String message) {
- return noteOpNoThrow(strOpToOp(op), uid, packageName, featureId, message);
+ @Nullable String attributionTag, @Nullable String message) {
+ return noteOpNoThrow(strOpToOp(op), uid, packageName, attributionTag, message);
}
/**
@@ -7140,7 +7312,8 @@
* @param op The operation to note. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The feature in the app or {@code null} for default feature
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
+ * null} for default attribution
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7150,7 +7323,7 @@
* @hide
*/
public int noteOpNoThrow(int op, int uid, @Nullable String packageName,
- @Nullable String featureId, @Nullable String message) {
+ @Nullable String attributionTag, @Nullable String message) {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
@@ -7161,14 +7334,14 @@
}
}
- int mode = mService.noteOperation(op, uid, packageName, featureId,
+ int mode = mService.noteOperation(op, uid, packageName, attributionTag,
collectionMode == COLLECT_ASYNC, message);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
- collectNotedOpForSelf(op, featureId);
+ collectNotedOpForSelf(op, attributionTag);
} else if (collectionMode == COLLECT_SYNC) {
- collectNotedOpSync(op, featureId);
+ collectNotedOpSync(op, attributionTag);
}
}
@@ -7208,8 +7381,8 @@
* @param op The operation to note. One of the OP_* constants.
* @param proxiedPackageName The name of the application calling into the proxy application.
* @param proxiedUid The uid of the proxied application
- * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
- * feature
+ * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+ * attribution tag} or {@code null} for default attribution
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
@@ -7221,8 +7394,8 @@
* @hide
*/
public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
- @Nullable String proxiedFeatureId, @Nullable String message) {
- int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedFeatureId,
+ @Nullable String proxiedAttributionTag, @Nullable String message) {
+ int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedAttributionTag,
message);
if (mode == MODE_ERRORED) {
throw new SecurityException("Proxy package " + mContext.getOpPackageName()
@@ -7241,8 +7414,8 @@
* @param op The operation to note. One of the OPSTR_* constants.
* @param proxiedPackageName The name of the application calling into the proxy application.
* @param proxiedUid The uid of the proxied application
- * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
- * feature
+ * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+ * attribution tag} or {@code null} for default attribution
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
@@ -7252,8 +7425,8 @@
* op.
*/
public int noteProxyOp(@NonNull String op, @Nullable String proxiedPackageName, int proxiedUid,
- @Nullable String proxiedFeatureId, @Nullable String message) {
- return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedFeatureId,
+ @Nullable String proxiedAttributionTag, @Nullable String message) {
+ return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedAttributionTag,
message);
}
@@ -7284,14 +7457,14 @@
* @param op The op to note
* @param proxiedPackageName The package to note the op for
* @param proxiedUid The uid the package belongs to
- * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
- * feature
+ * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+ * attribution tag} or {@code null} for default attribution
* @param message A message describing the reason the op was noted
*/
public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
- int proxiedUid, @Nullable String proxiedFeatureId, @Nullable String message) {
+ int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid,
- proxiedFeatureId, message);
+ proxiedAttributionTag, message);
}
/**
@@ -7302,14 +7475,14 @@
* @param proxiedPackageName The package to note the op for or {@code null} if the op should be
* noted for the "android" package
* @param proxiedUid The uid the package belongs to
- * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
- * feature
+ * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
+ * attribution tag} or {@code null} for default attribution
* @param message A message describing the reason the op was noted
*
* @hide
*/
public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid,
- @Nullable String proxiedFeatureId, @Nullable String message) {
+ @Nullable String proxiedAttributionTag, @Nullable String message) {
int myUid = Process.myUid();
try {
@@ -7323,17 +7496,17 @@
}
int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
- proxiedFeatureId, myUid, mContext.getOpPackageName(),
- mContext.getFeatureId(), collectionMode == COLLECT_ASYNC, message);
+ proxiedAttributionTag, myUid, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
- collectNotedOpForSelf(op, proxiedFeatureId);
+ collectNotedOpForSelf(op, proxiedAttributionTag);
} else if (collectionMode == COLLECT_SYNC
// Only collect app-ops when the proxy is trusted
&& mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
myUid) == PackageManager.PERMISSION_GRANTED) {
- collectNotedOpSync(op, proxiedFeatureId);
+ collectNotedOpSync(op, proxiedAttributionTag);
}
}
@@ -7522,8 +7695,8 @@
* @param op The operation to start. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
- * null} for default feature
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
+ * {@code null} for default attribution
* @param message Description why op was started
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7534,8 +7707,8 @@
* the package is not in the passed in UID.
*/
public int startOp(@NonNull String op, int uid, @Nullable String packageName,
- @Nullable String featureId, @Nullable String message) {
- return startOp(strOpToOp(op), uid, packageName, false, featureId, message);
+ @Nullable String attributionTag, @Nullable String message) {
+ return startOp(strOpToOp(op), uid, packageName, false, attributionTag, message);
}
/**
@@ -7544,7 +7717,8 @@
* @param op The operation to start. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The feature in the app or {@code null} for default feature
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
+ * {@code null} for default attribution
* @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
* @param message Description why op was started
*
@@ -7558,8 +7732,8 @@
* @hide
*/
public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault,
- @Nullable String featureId, @Nullable String message) {
- final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, featureId,
+ @Nullable String attributionTag, @Nullable String message) {
+ final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, attributionTag,
message);
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
@@ -7602,7 +7776,8 @@
* @param op The operation to start. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The feature in the app or {@code null} for default feature
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
+ * {@code null} for default attribution
* @param message Description why op was started
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7610,8 +7785,8 @@
* causing the app to crash).
*/
public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
- @NonNull String featureId, @Nullable String message) {
- return startOpNoThrow(strOpToOp(op), uid, packageName, false, featureId, message);
+ @NonNull String attributionTag, @Nullable String message) {
+ return startOpNoThrow(strOpToOp(op), uid, packageName, false, attributionTag, message);
}
/**
@@ -7621,7 +7796,8 @@
* @param op The operation to start. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
- * @param featureId The feature in the app or {@code null} for default feature
+ * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
+ * {@code null} for default attribution
* @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
* @param message Description why op was started
*
@@ -7632,7 +7808,7 @@
* @hide
*/
public int startOpNoThrow(int op, int uid, @NonNull String packageName,
- boolean startIfModeDefault, @Nullable String featureId, @Nullable String message) {
+ boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message) {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
@@ -7644,13 +7820,13 @@
}
int mode = mService.startOperation(getClientId(), op, uid, packageName,
- featureId, startIfModeDefault, collectionMode == COLLECT_ASYNC, message);
+ attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
- collectNotedOpForSelf(op, featureId);
+ collectNotedOpForSelf(op, attributionTag);
} else if (collectionMode == COLLECT_SYNC) {
- collectNotedOpSync(op, featureId);
+ collectNotedOpSync(op, attributionTag);
}
}
@@ -7684,8 +7860,8 @@
* previously passed in when starting the operation.
*/
public void finishOp(@NonNull String op, int uid, @NonNull String packageName,
- @Nullable String featureId) {
- finishOp(strOpToOp(op), uid, packageName, featureId);
+ @Nullable String attributionTag) {
+ finishOp(strOpToOp(op), uid, packageName, attributionTag);
}
/**
@@ -7706,9 +7882,9 @@
* @hide
*/
public void finishOp(int op, int uid, @NonNull String packageName,
- @Nullable String featureId) {
+ @Nullable String attributionTag) {
try {
- mService.finishOperation(getClientId(), op, uid, packageName, featureId);
+ mService.finishOperation(getClientId(), op, uid, packageName, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -7819,15 +7995,15 @@
* Collect a noted op for the current process.
*
* @param op The noted op
- * @param featureId The feature the op is noted for
+ * @param attributionTag The attribution tag the op is noted for
*/
- private void collectNotedOpForSelf(int op, @Nullable String featureId) {
+ private void collectNotedOpForSelf(int op, @Nullable String attributionTag) {
synchronized (sLock) {
if (sOnOpNotedCallback != null) {
- sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, featureId));
+ sOnOpNotedCallback.onSelfNoted(new SyncNotedAppOp(op, attributionTag));
}
}
- sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
+ sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, attributionTag));
}
/**
@@ -7836,9 +8012,9 @@
* <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
*
* @param op The noted op
- * @param featureId The feature the op is noted for
+ * @param attributionTag The attribution tag the op is noted for
*/
- private void collectNotedOpSync(int op, @Nullable String featureId) {
+ private void collectNotedOpSync(int op, @Nullable String attributionTag) {
// If this is inside of a two-way binder call:
// We are inside of a two-way binder call. Delivered to caller via
// {@link #prefixParcelWithAppOpsIfNeeded}
@@ -7848,16 +8024,16 @@
sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
}
- long[] appOpsNotedForFeature = appOpsNoted.get(featureId);
- if (appOpsNotedForFeature == null) {
- appOpsNotedForFeature = new long[2];
- appOpsNoted.put(featureId, appOpsNotedForFeature);
+ long[] appOpsNotedForAttribution = appOpsNoted.get(attributionTag);
+ if (appOpsNotedForAttribution == null) {
+ appOpsNotedForAttribution = new long[2];
+ appOpsNoted.put(attributionTag, appOpsNotedForAttribution);
}
if (op < 64) {
- appOpsNotedForFeature[0] |= 1L << op;
+ appOpsNotedForAttribution[0] |= 1L << op;
} else {
- appOpsNotedForFeature[1] |= 1L << (op - 64);
+ appOpsNotedForAttribution[1] |= 1L << (op - 64);
}
}
@@ -7938,10 +8114,10 @@
p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
- int numFeatureWithNotesAppOps = notedAppOps.size();
- p.writeInt(numFeatureWithNotesAppOps);
+ int numAttributionWithNotesAppOps = notedAppOps.size();
+ p.writeInt(numAttributionWithNotesAppOps);
- for (int i = 0; i < numFeatureWithNotesAppOps; i++) {
+ for (int i = 0; i < numAttributionWithNotesAppOps; i++) {
p.writeString(notedAppOps.keyAt(i));
p.writeLong(notedAppOps.valueAt(i)[0]);
p.writeLong(notedAppOps.valueAt(i)[1]);
@@ -7959,10 +8135,10 @@
* @hide
*/
public static void readAndLogNotedAppops(@NonNull Parcel p) {
- int numFeaturesWithNotedAppOps = p.readInt();
+ int numAttributionsWithNotedAppOps = p.readInt();
- for (int i = 0; i < numFeaturesWithNotedAppOps; i++) {
- String featureId = p.readString();
+ for (int i = 0; i < numAttributionsWithNotedAppOps; i++) {
+ String attributionTag = p.readString();
long[] rawNotedAppOps = new long[2];
rawNotedAppOps[0] = p.readLong();
rawNotedAppOps[1] = p.readLong();
@@ -7974,13 +8150,13 @@
for (int code = notedAppOps.nextSetBit(0); code != -1;
code = notedAppOps.nextSetBit(code + 1)) {
if (sOnOpNotedCallback != null) {
- sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, featureId));
+ sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag));
}
}
}
for (int code = notedAppOps.nextSetBit(0); code != -1;
code = notedAppOps.nextSetBit(code + 1)) {
- sMessageCollector.onNoted(new SyncNotedAppOp(code, featureId));
+ sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag));
}
}
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6013473..0b0a803 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -840,6 +840,27 @@
}
@Override
+ public boolean setAutoRevokeWhitelisted(
+ @NonNull String packageName, boolean whitelisted) {
+ try {
+ final int userId = getUserId();
+ return mPermissionManager.setAutoRevokeWhitelisted(packageName, whitelisted, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public boolean isAutoRevokeWhitelisted(@NonNull String packageName) {
+ try {
+ final int userId = getUserId();
+ return mPermissionManager.isAutoRevokeWhitelisted(packageName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName,
@NonNull String permName, @PermissionWhitelistFlags int flags) {
try {
@@ -3328,6 +3349,15 @@
}
}
+ @Override
+ public boolean isAutoRevokeWhitelisted() {
+ try {
+ return mPM.isAutoRevokeWhitelisted(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
try {
mPM.setMimeGroup(mContext.getPackageName(), mimeGroup,
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 4d955db..b0c2762c 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -48,8 +48,8 @@
/** Uid that noted the op */
private final @IntRange(from = 0) int mNotingUid;
- /** {@link android.content.Context#createFeatureContext Feature} in the app */
- private final @Nullable String mFeatureId;
+ /** {@link android.content.Context#createAttributionContext attribution tag} */
+ private final @Nullable String mAttributionTag;
/** Message associated with the noteOp. This message is set by the app noting the op */
private final @NonNull String mMessage;
@@ -92,8 +92,8 @@
* Op that was noted
* @param notingUid
* Uid that noted the op
- * @param featureId
- * {@link android.content.Context#createFeatureContext Feature} in the app
+ * @param attributionTag
+ * {@link android.content.Context#createAttributionContext attribution tag}
* @param message
* Message associated with the noteOp. This message is set by the app noting the op
* @param time
@@ -104,7 +104,7 @@
public AsyncNotedAppOp(
@IntRange(from = 0) int opCode,
@IntRange(from = 0) int notingUid,
- @Nullable String featureId,
+ @Nullable String attributionTag,
@NonNull String message,
@CurrentTimeMillisLong long time) {
this.mOpCode = opCode;
@@ -115,7 +115,7 @@
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mNotingUid,
"from", 0);
- this.mFeatureId = featureId;
+ this.mAttributionTag = attributionTag;
this.mMessage = message;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMessage);
@@ -135,11 +135,11 @@
}
/**
- * {@link android.content.Context#createFeatureContext Feature} in the app
+ * {@link android.content.Context#createAttributionContext attribution tag}
*/
@DataClass.Generated.Member
- public @Nullable String getFeatureId() {
- return mFeatureId;
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
}
/**
@@ -173,7 +173,7 @@
return true
&& mOpCode == that.mOpCode
&& mNotingUid == that.mNotingUid
- && java.util.Objects.equals(mFeatureId, that.mFeatureId)
+ && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
&& java.util.Objects.equals(mMessage, that.mMessage)
&& mTime == that.mTime;
}
@@ -187,7 +187,7 @@
int _hash = 1;
_hash = 31 * _hash + mOpCode;
_hash = 31 * _hash + mNotingUid;
- _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
_hash = 31 * _hash + java.util.Objects.hashCode(mMessage);
_hash = 31 * _hash + Long.hashCode(mTime);
return _hash;
@@ -200,11 +200,11 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mFeatureId != null) flg |= 0x4;
+ if (mAttributionTag != null) flg |= 0x4;
dest.writeByte(flg);
dest.writeInt(mOpCode);
dest.writeInt(mNotingUid);
- if (mFeatureId != null) dest.writeString(mFeatureId);
+ if (mAttributionTag != null) dest.writeString(mAttributionTag);
dest.writeString(mMessage);
dest.writeLong(mTime);
}
@@ -223,7 +223,7 @@
byte flg = in.readByte();
int opCode = in.readInt();
int notingUid = in.readInt();
- String featureId = (flg & 0x4) == 0 ? null : in.readString();
+ String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
String message = in.readString();
long time = in.readLong();
@@ -235,7 +235,7 @@
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mNotingUid,
"from", 0);
- this.mFeatureId = featureId;
+ this.mAttributionTag = attributionTag;
this.mMessage = message;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMessage);
@@ -261,10 +261,10 @@
};
@DataClass.Generated(
- time = 1583866178330L,
+ time = 1583866239013L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate void onConstructed()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.IntRange(from=0L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.CurrentTimeMillisLong long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nprivate void onConstructed()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2873b10..9ccfe8d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -219,8 +219,8 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mOpPackageName;
- /** If of feature this context is for */
- private final @Nullable String mFeatureId;
+ /** Attribution tag of this context */
+ private final @Nullable String mAttributionTag;
private final @NonNull ResourcesManager mResourcesManager;
@UnsupportedAppUsage
@@ -421,8 +421,8 @@
/** @hide */
@Override
- public @Nullable String getFeatureId() {
- return mFeatureId;
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
}
@Override
@@ -1026,10 +1026,10 @@
public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
try {
ActivityTaskManager.getService().startActivityAsUser(
- mMainThread.getApplicationThread(), getBasePackageName(), getFeatureId(), intent,
- intent.resolveTypeIfNeeded(getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
- user.getIdentifier());
+ mMainThread.getApplicationThread(), getBasePackageName(), getAttributionTag(),
+ intent, intent.resolveTypeIfNeeded(getContentResolver()),
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
+ user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1109,9 +1109,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
- getUserId());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
+ false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1126,9 +1126,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
- null, false, false, getUserId());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions,
+ AppOpsManager.OP_NONE, null, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1141,9 +1141,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
- null, false, false, getUserId());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions,
+ AppOpsManager.OP_NONE, null, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1156,9 +1156,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
- null, false, false, user.getIdentifier());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions,
+ AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1173,9 +1173,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
- options, false, false, getUserId());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions,
+ AppOpsManager.OP_NONE, options, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1190,9 +1190,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
- getUserId());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false,
+ false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1207,9 +1207,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
- null, true, false, getUserId());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions,
+ AppOpsManager.OP_NONE, null, true, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1270,8 +1270,8 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
- initialCode, initialData, initialExtras, receiverPermissions, appOp,
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ rd, initialCode, initialData, initialExtras, receiverPermissions, appOp,
options, true, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1284,9 +1284,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
- user.getIdentifier());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
+ false, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1307,9 +1307,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
- options, false, false, user.getIdentifier());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions,
+ AppOpsManager.OP_NONE, options, false, false, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1324,9 +1324,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
- user.getIdentifier());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false,
+ false, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1375,9 +1375,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
- initialCode, initialData, initialExtras, receiverPermissions,
- appOp, options, true, false, user.getIdentifier());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ rd, initialCode, initialData, initialExtras, receiverPermissions, appOp,
+ options, true, false, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1416,9 +1416,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
- getUserId());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
+ true, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1452,9 +1452,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
- initialCode, initialData, initialExtras, null,
- AppOpsManager.OP_NONE, null, true, true, getUserId());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null,
+ true, true, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1484,9 +1484,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
- user.getIdentifier());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
+ true, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1499,9 +1499,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
- user.getIdentifier());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options,
+ false, true, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1534,9 +1534,9 @@
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
- mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
- initialCode, initialData, initialExtras, null,
- AppOpsManager.OP_NONE, null, true, true, user.getIdentifier());
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null,
+ true, true, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1620,7 +1620,7 @@
}
try {
final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
- mMainThread.getApplicationThread(), mBasePackageName, getFeatureId(), rd,
+ mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
filter, broadcastPermission, userId, flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
@@ -1696,7 +1696,7 @@
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
- getOpPackageName(), getFeatureId(), user.getIdentifier());
+ getOpPackageName(), getAttributionTag(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
@@ -1899,8 +1899,9 @@
public Object getSystemService(String name) {
// Check incorrect Context usage.
if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
- final String errorMessage = "Tried to access visual service " + name
- + " from a non-visual Context.";
+ final String errorMessage = "Tried to access visual service "
+ + SystemServiceRegistry.getSystemServiceClassName(name)
+ + " from a non-visual Context. ";
final String message = "Visual services, such as WindowManager, WallpaperService or "
+ "LayoutInflater should be accessed from Activity or other visual Context. "
+ "Use an Activity or a Context created with "
@@ -2285,14 +2286,14 @@
if (packageName.equals("system") || packageName.equals("android")) {
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
- return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, null,
+ return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, null,
mToken, user, flags, null, null);
}
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, mFeatureId, null,
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, mAttributionTag, null,
mToken, user, flags, null, null);
final int displayId = getDisplayId();
@@ -2329,7 +2330,7 @@
final String[] paths = mPackageInfo.getSplitPaths(splitName);
final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
- mFeatureId, splitName, mToken, mUser, mFlags, classLoader, null);
+ mAttributionTag, splitName, mToken, mUser, mFlags, classLoader, null);
final int displayId = getDisplayId();
@@ -2353,7 +2354,7 @@
throw new IllegalArgumentException("overrideConfiguration must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+ ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = getDisplayId();
@@ -2370,7 +2371,7 @@
throw new IllegalArgumentException("display must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+ ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = display.getDisplayId();
@@ -2394,7 +2395,7 @@
}
ContextImpl createBaseWindowContext(IBinder token) {
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+ ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
mSplitName, token, mUser, mFlags, mClassLoader, null);
context.mIsUiContext = true;
@@ -2420,8 +2421,8 @@
}
@Override
- public @NonNull Context createFeatureContext(@Nullable String featureId) {
- return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName,
+ public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
+ return new ContextImpl(this, mMainThread, mPackageInfo, attributionTag, mSplitName,
mToken, mUser, mFlags, mClassLoader, null);
}
@@ -2429,7 +2430,7 @@
public Context createDeviceProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
+ return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName,
mToken, mUser, flags, mClassLoader, null);
}
@@ -2437,7 +2438,7 @@
public Context createCredentialProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
+ return new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName,
mToken, mUser, flags, mClassLoader, null);
}
@@ -2700,7 +2701,7 @@
}
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
- @NonNull LoadedApk packageInfo, @Nullable String featureId,
+ @NonNull LoadedApk packageInfo, @Nullable String attributionTag,
@Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user,
int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
mOuterContext = this;
@@ -2754,7 +2755,7 @@
}
mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
- mFeatureId = featureId;
+ mAttributionTag = attributionTag;
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 34684c4..4cb8d93 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -175,11 +175,4 @@
* Called from SystemUI when it shows the AoD UI.
*/
oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
-
- /**
- * Called when the wallpaper needs to zoom out.
- * The zoom value goes from 0 to 1 (inclusive) where 1 means fully zoomed out,
- * 0 means fully zoomed in
- */
- oneway void setWallpaperZoomOut(float zoom, String callingPackage, int displayId);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 18932c6..818a121 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1720,11 +1720,10 @@
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
- int result = ActivityTaskManager.getService()
- .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent,
- intent.resolveTypeIfNeeded(who.getContentResolver()),
- token, target != null ? target.mEmbeddedID : null,
- requestCode, 0, null, options);
+ int result = ActivityTaskManager.getService().startActivity(whoThread,
+ who.getBasePackageName(), who.getAttributionTag(), intent,
+ intent.resolveTypeIfNeeded(who.getContentResolver()), token,
+ target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
@@ -1793,9 +1792,9 @@
intents[i].prepareToLeaveProcess(who);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
}
- int result = ActivityTaskManager.getService()
- .startActivities(whoThread, who.getBasePackageName(), who.getFeatureId(), intents,
- resolvedTypes, token, options, userId);
+ int result = ActivityTaskManager.getService().startActivities(whoThread,
+ who.getBasePackageName(), who.getAttributionTag(), intents, resolvedTypes,
+ token, options, userId);
checkStartActivityResult(result, intents[0]);
return result;
} catch (RemoteException e) {
@@ -1860,10 +1859,10 @@
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
- int result = ActivityTaskManager.getService()
- .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent,
- intent.resolveTypeIfNeeded(who.getContentResolver()),
- token, target, requestCode, 0, null, options);
+ int result = ActivityTaskManager.getService().startActivity(whoThread,
+ who.getBasePackageName(), who.getAttributionTag(), intent,
+ intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
+ requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
@@ -1927,11 +1926,10 @@
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
- int result = ActivityTaskManager.getService()
- .startActivityAsUser(whoThread, who.getBasePackageName(), who.getFeatureId(),
- intent, intent.resolveTypeIfNeeded(who.getContentResolver()),
- token, resultWho,
- requestCode, 0, null, options, user.getIdentifier());
+ int result = ActivityTaskManager.getService().startActivityAsUser(whoThread,
+ who.getBasePackageName(), who.getAttributionTag(), intent,
+ intent.resolveTypeIfNeeded(who.getContentResolver()), token, resultWho,
+ requestCode, 0, null, options, user.getIdentifier());
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
@@ -2022,7 +2020,7 @@
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
- who.getFeatureId(), intent,
+ who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 32e7d84..864af3d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
+import static com.android.internal.widget.ConversationLayout.CONVERSATION_LAYOUT_ENABLED;
import android.annotation.ColorInt;
import android.annotation.DimenRes;
@@ -389,6 +390,7 @@
STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text);
STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox);
STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_conversation);
STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
STANDARD_LAYOUTS.add(R.layout.notification_template_header);
@@ -5138,7 +5140,7 @@
int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
contentView.setDrawableTint(R.id.expand_button, false, color,
PorterDuff.Mode.SRC_ATOP);
- contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
+ contentView.setInt(R.id.expand_button, "setOriginalNotificationColor",
color);
}
@@ -6116,7 +6118,9 @@
}
private int getMessagingLayoutResource() {
- return R.layout.notification_template_material_messaging;
+ return CONVERSATION_LAYOUT_ENABLED
+ ? R.layout.notification_template_material_conversation
+ : R.layout.notification_template_material_messaging;
}
private int getActionLayoutResource() {
@@ -6221,6 +6225,17 @@
}
return loadHeaderAppName();
}
+
+ /**
+ * @return if this builder uses a template
+ *
+ * @hide
+ */
+ public boolean usesTemplate() {
+ return (mN.contentView == null && mN.headsUpContentView == null
+ && mN.bigContentView == null)
+ || (mStyle != null && mStyle.displayCustomViewInline());
+ }
}
/**
@@ -7379,7 +7394,7 @@
public RemoteViews makeContentView(boolean increasedHeight) {
mBuilder.mOriginalActions = mBuilder.mActions;
mBuilder.mActions = new ArrayList<>();
- RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
+ RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */,
false /* hideLargeIcon */);
mBuilder.mActions = mBuilder.mOriginalActions;
mBuilder.mOriginalActions = null;
@@ -7469,19 +7484,18 @@
*/
@Override
public RemoteViews makeBigContentView() {
- return makeMessagingView(false /* displayImagesAtEnd */, true /* hideLargeIcon */);
+ return makeMessagingView(false /* isCollapsed */, true /* hideLargeIcon */);
}
/**
* Create a messaging layout.
*
- * @param displayImagesAtEnd should images be displayed at the end of the content instead
- * of inline.
+ * @param isCollapsed Should this use the collapsed layout
* @param hideRightIcons Should the reply affordance be shown at the end of the notification
* @return the created remoteView.
*/
@NonNull
- private RemoteViews makeMessagingView(boolean displayImagesAtEnd, boolean hideRightIcons) {
+ private RemoteViews makeMessagingView(boolean isCollapsed, boolean hideRightIcons) {
CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
? super.mBigContentTitle
: mConversationTitle;
@@ -7512,9 +7526,11 @@
p,
bindResult);
addExtras(mBuilder.mN.extras);
- // also update the end margin if there is an image
- contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
- bindResult.getIconMarginEnd());
+ if (!CONVERSATION_LAYOUT_ENABLED) {
+ // also update the end margin if there is an image
+ contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
+ bindResult.getIconMarginEnd());
+ }
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
: mBuilder.resolveContrastColor(p));
@@ -7522,14 +7538,21 @@
mBuilder.getPrimaryTextColor(p));
contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor",
mBuilder.getSecondaryTextColor(p));
- contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
- displayImagesAtEnd);
+ contentView.setInt(R.id.status_bar_latest_event_content,
+ "setNotificationBackgroundColor",
+ mBuilder.resolveBackgroundColor(p));
+ contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
+ isCollapsed);
contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
avatarReplacement);
contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
nameReplacement);
contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne",
isOneToOne);
+ contentView.setCharSequence(R.id.status_bar_latest_event_content,
+ "setConversationTitle", conversationTitle);
+ contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
+ mBuilder.mN.mLargeIcon);
contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
mBuilder.mN.extras);
return contentView;
@@ -7590,9 +7613,11 @@
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
+ RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */,
true /* hideLargeIcon */);
- remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
+ if (!CONVERSATION_LAYOUT_ENABLED) {
+ remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
+ }
return remoteViews;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index cbbdf63..811b9c0 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1015,14 +1015,15 @@
}
/**
+ * Returns the currently applied notification policy.
+ *
* <p>
- * Gets the currently applied notification policy. If {@link #getCurrentInterruptionFilter}
- * is equal to {@link #INTERRUPTION_FILTER_ALL}, then the consolidated notification policy
- * will match the default notification policy returned by {@link #getNotificationPolicy}.
+ * If {@link #getCurrentInterruptionFilter} is equal to {@link #INTERRUPTION_FILTER_ALL},
+ * then the consolidated notification policy will match the default notification policy
+ * returned by {@link #getNotificationPolicy}.
* </p>
*/
- @Nullable
- public NotificationManager.Policy getConsolidatedNotificationPolicy() {
+ public @NonNull NotificationManager.Policy getConsolidatedNotificationPolicy() {
INotificationManager service = getService();
try {
return service.getConsolidatedNotificationPolicy();
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index f68c929..792f840 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -355,8 +355,8 @@
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
- null, null, requestCode, new Intent[] { intent },
+ ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, options, context.getUserId());
return target != null ? new PendingIntent(target) : null;
@@ -381,8 +381,8 @@
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
- null, null, requestCode, new Intent[] { intent },
+ ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, options, user.getIdentifier());
return target != null ? new PendingIntent(target) : null;
@@ -498,9 +498,9 @@
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
- null, null, requestCode, intents, resolvedTypes, flags, options,
- context.getUserId());
+ ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes,
+ flags, options, context.getUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -524,8 +524,8 @@
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
- null, null, requestCode, intents, resolvedTypes,
+ ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes,
flags, options, user.getIdentifier());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
@@ -576,8 +576,8 @@
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_BROADCAST, packageName, context.getFeatureId(),
- null, null, requestCode, new Intent[] { intent },
+ ActivityManager.INTENT_SENDER_BROADCAST, packageName,
+ context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, userHandle.getIdentifier());
return target != null ? new PendingIntent(target) : null;
@@ -655,7 +655,7 @@
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
- serviceKind, packageName, context.getFeatureId(),
+ serviceKind, packageName, context.getAttributionTag(),
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, context.getUserId());
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 81671c3..9f5dee9 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -250,28 +250,6 @@
return dm;
}
- private static void applyNonDefaultDisplayMetricsToConfiguration(
- @NonNull DisplayMetrics dm, @NonNull Configuration config) {
- config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
- config.densityDpi = dm.densityDpi;
- config.screenWidthDp = (int) (dm.widthPixels / dm.density);
- config.screenHeightDp = (int) (dm.heightPixels / dm.density);
- int sl = Configuration.resetScreenLayout(config.screenLayout);
- if (dm.widthPixels > dm.heightPixels) {
- config.orientation = Configuration.ORIENTATION_LANDSCAPE;
- config.screenLayout = Configuration.reduceScreenLayout(sl,
- config.screenWidthDp, config.screenHeightDp);
- } else {
- config.orientation = Configuration.ORIENTATION_PORTRAIT;
- config.screenLayout = Configuration.reduceScreenLayout(sl,
- config.screenHeightDp, config.screenWidthDp);
- }
- config.smallestScreenWidthDp = Math.min(config.screenWidthDp, config.screenHeightDp);
- config.compatScreenWidthDp = config.screenWidthDp;
- config.compatScreenHeightDp = config.screenHeightDp;
- config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
- }
-
public boolean applyCompatConfigurationLocked(int displayDensity,
@NonNull Configuration compatConfiguration) {
if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
@@ -519,17 +497,11 @@
private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
Configuration config;
- final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY);
final boolean hasOverrideConfig = key.hasOverrideConfiguration();
- if (!isDefaultDisplay || hasOverrideConfig) {
+ if (hasOverrideConfig) {
config = new Configuration(getConfiguration());
- if (!isDefaultDisplay) {
- applyNonDefaultDisplayMetricsToConfiguration(dm, config);
- }
- if (hasOverrideConfig) {
- config.updateFrom(key.mOverrideConfiguration);
- if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
- }
+ config.updateFrom(key.mOverrideConfiguration);
+ if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
} else {
config = getConfiguration();
}
@@ -1110,8 +1082,6 @@
+ resourcesImpl + " config to: " + config);
}
int displayId = key.mDisplayId;
- final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
- tmpConfig.setTo(config);
// Get new DisplayMetrics based on the DisplayAdjustments given to the ResourcesImpl. Update
// a copy if the CompatibilityInfo changed, because the ResourcesImpl object will handle the
@@ -1121,15 +1091,12 @@
daj = new DisplayAdjustments(daj);
daj.setCompatibilityInfo(compat);
}
- daj.setConfiguration(config);
- DisplayMetrics dm = getDisplayMetrics(displayId, daj);
- if (displayId != Display.DEFAULT_DISPLAY) {
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
- }
-
- if (hasOverrideConfiguration) {
+ tmpConfig.setTo(config);
+ if (key.hasOverrideConfiguration()) {
tmpConfig.updateFrom(key.mOverrideConfiguration);
}
+ daj.setConfiguration(tmpConfig);
+ DisplayMetrics dm = getDisplayMetrics(displayId, daj);
resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
}
diff --git a/core/java/android/app/RuntimeAppOpAccessMessage.java b/core/java/android/app/RuntimeAppOpAccessMessage.java
index a81b8e7..a19f815 100644
--- a/core/java/android/app/RuntimeAppOpAccessMessage.java
+++ b/core/java/android/app/RuntimeAppOpAccessMessage.java
@@ -44,7 +44,7 @@
/** Name of package for which runtime app op access message was collected */
private final @NonNull String mPackageName;
/** Feature of package for which runtime app op access message was collected */
- private final @Nullable String mFeatureId;
+ private final @Nullable String mAttributionTag;
/** Message collected (including stacktrace for synchronous ops) */
private final @NonNull String mMessage;
/** Sampling strategy used to collect this message. */
@@ -63,8 +63,8 @@
* Op code of operation access which was collected
* @param packageName
* Name of package for which runtime app op access message was collected
- * @param featureId
- * Feature of package for which runtime app op access message was collected
+ * @param attributionTag
+ * Attribution tag for which runtime app op access message was collected
* @param message
* Message collected (including stacktrace for synchronous ops)
* @param samplingStrategy
@@ -75,7 +75,7 @@
@IntRange(from = 0L) int uid,
@IntRange(from = 0L) int opCode,
@NonNull String packageName,
- @Nullable String featureId,
+ @Nullable String attributionTag,
@NonNull String message,
@AppOpsManager.SamplingStrategy int samplingStrategy) {
this.mUid = uid;
@@ -90,7 +90,7 @@
this.mPackageName = packageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
- this.mFeatureId = featureId;
+ this.mAttributionTag = attributionTag;
this.mMessage = message;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMessage);
@@ -134,11 +134,11 @@
}
/**
- * Feature of package for which runtime app op access message was collected
+ * Attribution tag for which runtime app op access message was collected
*/
@DataClass.Generated.Member
- public @Nullable String getFeatureId() {
- return mFeatureId;
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
}
/**
@@ -164,12 +164,12 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mFeatureId != null) flg |= 0x8;
+ if (mAttributionTag != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mUid);
dest.writeInt(mOpCode);
dest.writeString(mPackageName);
- if (mFeatureId != null) dest.writeString(mFeatureId);
+ if (mAttributionTag != null) dest.writeString(mAttributionTag);
dest.writeString(mMessage);
dest.writeInt(mSamplingStrategy);
}
@@ -189,7 +189,7 @@
int uid = in.readInt();
int opCode = in.readInt();
String packageName = in.readString();
- String featureId = (flg & 0x8) == 0 ? null : in.readString();
+ String attributionTag = (flg & 0x8) == 0 ? null : in.readString();
String message = in.readString();
int samplingStrategy = in.readInt();
@@ -205,7 +205,7 @@
this.mPackageName = packageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
- this.mFeatureId = featureId;
+ this.mAttributionTag = attributionTag;
this.mMessage = message;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMessage);
@@ -234,7 +234,7 @@
time = 1581517099127L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/
+ inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 42563b5..1329fa4 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -399,7 +399,6 @@
* {@code false}, re-enables expansion of the status bar.
* @hide
*/
- @SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
public void setDisabledForSimNetworkLock(boolean disabled) {
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index 13b90ca..0a880dc 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -43,24 +43,24 @@
/** op code of synchronous appop noted */
private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
- /** featureId of synchronous appop noted */
- private final @Nullable String mFeatureId;
+ /** attributionTag of synchronous appop noted */
+ private final @Nullable String mAttributionTag;
/**
* Creates a new SyncNotedAppOp.
*
* @param opCode
* op code of synchronous appop noted
- * @param featureId
- * featureId of synchronous appop noted
+ * @param attributionTag
+ * attributionTag of synchronous appop noted
*/
- public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String featureId) {
+ public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag) {
this.mOpCode = opCode;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mOpCode,
"from", 0,
"to", AppOpsManager._NUM_OP - 1);
- this.mFeatureId = featureId;
+ this.mAttributionTag = attributionTag;
}
/**
@@ -84,11 +84,11 @@
/**
- * featureId of synchronous appop noted
+ * attributionTag of synchronous appop noted
*/
@DataClass.Generated.Member
- public @Nullable String getFeatureId() {
- return mFeatureId;
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
}
@Override
@@ -105,7 +105,7 @@
//noinspection PointlessBooleanExpression
return true
&& mOpCode == that.mOpCode
- && java.util.Objects.equals(mFeatureId, that.mFeatureId);
+ && java.util.Objects.equals(mAttributionTag, that.mAttributionTag);
}
@Override
@@ -116,7 +116,7 @@
int _hash = 1;
_hash = 31 * _hash + mOpCode;
- _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
return _hash;
}
@@ -127,10 +127,10 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mFeatureId != null) flg |= 0x2;
+ if (mAttributionTag != null) flg |= 0x2;
dest.writeByte(flg);
dest.writeInt(mOpCode);
- if (mFeatureId != null) dest.writeString(mFeatureId);
+ if (mAttributionTag != null) dest.writeString(mAttributionTag);
}
@Override
@@ -146,14 +146,14 @@
byte flg = in.readByte();
int opCode = in.readInt();
- String featureId = (flg & 0x2) == 0 ? null : in.readString();
+ String attributionTag = (flg & 0x2) == 0 ? null : in.readString();
this.mOpCode = opCode;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mOpCode,
"from", 0,
"to", AppOpsManager._NUM_OP - 1);
- this.mFeatureId = featureId;
+ this.mAttributionTag = attributionTag;
// onConstructed(); // You can define this method to get a callback
}
@@ -176,7 +176,7 @@
time = 1579188889960L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/
+ inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index a3bcc9c..e8f30df 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -19,6 +19,7 @@
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.ContextImpl.ServiceInitializationState;
import android.app.admin.DevicePolicyManager;
@@ -227,6 +228,8 @@
new ArrayMap<Class<?>, String>();
private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new ArrayMap<String, ServiceFetcher<?>>();
+ private static final Map<String, String> SYSTEM_SERVICE_CLASS_NAMES = new ArrayMap<>();
+
private static int sServiceCacheSize;
private static volatile boolean sInitializing;
@@ -1389,6 +1392,19 @@
@NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
+ SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
+ }
+
+ /**
+ * Returns system service class name by system service name. This method is mostly an inverse of
+ * {@link #getSystemServiceName(Class)}
+ *
+ * @return system service class name. {@code null} if service name is invalid.
+ * @hide
+ */
+ @Nullable
+ public static String getSystemServiceClassName(@NonNull String name) {
+ return SYSTEM_SERVICE_CLASS_NAMES.get(name);
}
/**
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 1a84547..b2dd0ef 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -23,6 +23,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.RemoteException;
@@ -161,6 +162,13 @@
*/
public @WindowConfiguration.ActivityType int topActivityType;
+ /**
+ * The {@link ActivityInfo} of the top activity in this task.
+ * @hide
+ */
+ @Nullable
+ public ActivityInfo topActivityInfo;
+
TaskInfo() {
// Do nothing
}
@@ -217,8 +225,11 @@
token = IWindowContainer.Stub.asInterface(source.readStrongBinder());
topActivityType = source.readInt();
pictureInPictureParams = source.readInt() != 0
- ? PictureInPictureParams.CREATOR.createFromParcel(source)
- : null;
+ ? PictureInPictureParams.CREATOR.createFromParcel(source)
+ : null;
+ topActivityInfo = source.readInt() != 0
+ ? ActivityInfo.CREATOR.createFromParcel(source)
+ : null;
}
/**
@@ -262,6 +273,12 @@
dest.writeInt(1);
pictureInPictureParams.writeToParcel(dest, flags);
}
+ if (topActivityInfo == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ topActivityInfo.writeToParcel(dest, flags);
+ }
}
@Override
@@ -278,6 +295,7 @@
+ " resizeMode=" + resizeMode
+ " token=" + token
+ " topActivityType=" + topActivityType
- + " pictureInPictureParams=" + pictureInPictureParams;
+ + " pictureInPictureParams=" + pictureInPictureParams
+ + " topActivityInfo=" + topActivityInfo;
}
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index d9405e1..1b1568a 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -478,7 +478,7 @@
try {
Bundle params = new Bundle();
ParcelFileDescriptor pfd = mService.getWallpaperWithFeature(
- context.getOpPackageName(), context.getFeatureId(), this, FLAG_SYSTEM,
+ context.getOpPackageName(), context.getAttributionTag(), this, FLAG_SYSTEM,
params, userId);
if (pfd != null) {
@@ -1069,7 +1069,7 @@
try {
Bundle outParams = new Bundle();
return sGlobals.mService.getWallpaperWithFeature(mContext.getOpPackageName(),
- mContext.getFeatureId(), null, which, outParams, userId);
+ mContext.getAttributionTag(), null, which, outParams, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SecurityException e) {
@@ -1858,13 +1858,12 @@
*
* @hide
*/
- public void setWallpaperZoomOut(float zoom) {
+ public void setWallpaperZoomOut(IBinder windowToken, float zoom) {
if (zoom < 0 || zoom > 1f) {
throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom);
}
try {
- sGlobals.mService.setWallpaperZoomOut(zoom, mContext.getOpPackageName(),
- mContext.getDisplayId());
+ WindowManagerGlobal.getWindowSession().setWallpaperZoomOut(windowToken, zoom);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c532279..32e815e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5215,6 +5215,10 @@
* <p>Because this method might take several seconds to complete, it should only be called from
* a worker thread. This method returns {@code null} when called from the main thread.
*
+ * <p>This method is not thread-safe, calling it from multiple threads at the same time will
+ * result in undefined behavior. If the calling thread is interrupted while the invocation is
+ * in-flight, it will eventually terminate and return {@code null}.
+ *
* <p>Note: If the provided {@code alias} is of an existing alias, all former grants that apps
* have been given to access the key and certificates associated with this alias will be
* revoked.
@@ -5732,6 +5736,10 @@
throwIfParentInstance("isAlwaysOnVpnLockdownEnabled");
if (mService != null) {
try {
+ // Starting from Android R, the caller can pass the permission check in
+ // DevicePolicyManagerService if it holds android.permission.MAINLINE_NETWORK_STACK.
+ // Note that the android.permission.MAINLINE_NETWORK_STACK is a signature permission
+ // which is used by the NetworkStack mainline module.
return mService.isAlwaysOnVpnLockdownEnabled(admin);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -6001,7 +6009,7 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param required Whether auto time is set required or not.
* @throws SecurityException if {@code admin} is not a device owner.
- * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #setAutoTime}
+ * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #setAutoTimeEnabled}
* to turn auto time on or off and use {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
* to prevent the user from changing this setting.
*/
@@ -6019,7 +6027,7 @@
/**
* @return true if auto time is required.
- * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #getAutoTime}
+ * @deprecated From {@link android.os.Build.VERSION_CODES#R}. Use {@link #getAutoTimeEnabled}
*/
@Deprecated
public boolean getAutoTimeRequired() {
@@ -6049,10 +6057,10 @@
* @throws SecurityException if caller is not a device owner, a profile owner for the
* primary user, or a profile owner of an organization-owned managed profile.
*/
- public void setAutoTime(@NonNull ComponentName admin, boolean enabled) {
+ public void setAutoTimeEnabled(@NonNull ComponentName admin, boolean enabled) {
if (mService != null) {
try {
- mService.setAutoTime(admin, enabled);
+ mService.setAutoTimeEnabled(admin, enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -6064,10 +6072,10 @@
* @throws SecurityException if caller is not a device owner, a profile owner for the
* primary user, or a profile owner of an organization-owned managed profile.
*/
- public boolean getAutoTime(@NonNull ComponentName admin) {
+ public boolean getAutoTimeEnabled(@NonNull ComponentName admin) {
if (mService != null) {
try {
- return mService.getAutoTime(admin);
+ return mService.getAutoTimeEnabled(admin);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -6090,11 +6098,11 @@
* @throws SecurityException if caller is not a device owner, a profile owner for the
* primary user, or a profile owner of an organization-owned managed profile.
*/
- public void setAutoTimeZone(@NonNull ComponentName admin, boolean enabled) {
+ public void setAutoTimeZoneEnabled(@NonNull ComponentName admin, boolean enabled) {
throwIfParentInstance("setAutoTimeZone");
if (mService != null) {
try {
- mService.setAutoTimeZone(admin, enabled);
+ mService.setAutoTimeZoneEnabled(admin, enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -6106,11 +6114,11 @@
* @throws SecurityException if caller is not a device owner, a profile owner for the
* primary user, or a profile owner of an organization-owned managed profile.
*/
- public boolean getAutoTimeZone(@NonNull ComponentName admin) {
+ public boolean getAutoTimeZoneEnabled(@NonNull ComponentName admin) {
throwIfParentInstance("getAutoTimeZone");
if (mService != null) {
try {
- return mService.getAutoTimeZone(admin);
+ return mService.getAutoTimeZoneEnabled(admin);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8804,10 +8812,11 @@
* <p>
* The following settings used to be supported, but can be controlled in other ways:
* <ul>
- * <li>{@link android.provider.Settings.Global#AUTO_TIME} : Use {@link #setAutoTime} and
+ * <li>{@link android.provider.Settings.Global#AUTO_TIME} : Use {@link #setAutoTimeEnabled} and
* {@link UserManager#DISALLOW_CONFIG_DATE_TIME} instead.</li>
- * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE} : Use {@link #setAutoTimeZone}
- * and {@link UserManager#DISALLOW_CONFIG_DATE_TIME} instead.</li>
+ * <li>{@link android.provider.Settings.Global#AUTO_TIME_ZONE} : Use
+ * {@link #setAutoTimeZoneEnabled} and {@link UserManager#DISALLOW_CONFIG_DATE_TIME}
+ * instead.</li>
* <li>{@link android.provider.Settings.Global#DATA_ROAMING} : Use
* {@link UserManager#DISALLOW_DATA_ROAMING} instead.</li>
* </ul>
@@ -11847,18 +11856,19 @@
}
/**
- * Called by Device owner to set packages as protected. User will not be able to clear app
- * data or force-stop protected packages.
+ * Called by Device owner to disable user control over apps. User will not be able to clear
+ * app data or force-stop packages.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
- * @param packages The package names to protect.
+ * @param packages The package names for the apps.
* @throws SecurityException if {@code admin} is not a device owner.
*/
- public void setProtectedPackages(@NonNull ComponentName admin, @NonNull List<String> packages) {
- throwIfParentInstance("setProtectedPackages");
+ public void setUserControlDisabledPackages(@NonNull ComponentName admin,
+ @NonNull List<String> packages) {
+ throwIfParentInstance("setUserControlDisabledPackages");
if (mService != null) {
try {
- mService.setProtectedPackages(admin, packages);
+ mService.setUserControlDisabledPackages(admin, packages);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -11866,16 +11876,16 @@
}
/**
- * Returns the list of packages protected by the device owner.
+ * Returns the list of packages over which user control is disabled by the device owner.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @throws SecurityException if {@code admin} is not a device owner.
*/
- public @NonNull List<String> getProtectedPackages(@NonNull ComponentName admin) {
- throwIfParentInstance("getProtectedPackages");
+ public @NonNull List<String> getUserControlDisabledPackages(@NonNull ComponentName admin) {
+ throwIfParentInstance("getUserControlDisabledPackages");
if (mService != null) {
try {
- return mService.getProtectedPackages(admin);
+ return mService.getUserControlDisabledPackages(admin);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -11909,13 +11919,17 @@
}
/**
- * Called by device owner or profile owner of an organization-owned managed profile to return
- * whether Common Criteria mode is currently enabled for the device.
+ * Returns whether Common Criteria mode is currently enabled. Device owner and profile owner of
+ * an organization-owned managed profile can query its own Common Criteria mode setting by
+ * calling this method with its admin {@link ComponentName}. Any caller can obtain the
+ * aggregated device-wide Common Criteria mode state by passing {@code null} as the
+ * {@code admin} argument.
*
- * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if the caller is not a device admin.
* @return {@code true} if Common Criteria mode is enabled, {@code false} otherwise.
*/
- public boolean isCommonCriteriaModeEnabled(@NonNull ComponentName admin) {
+ public boolean isCommonCriteriaModeEnabled(@Nullable ComponentName admin) {
throwIfParentInstance("isCommonCriteriaModeEnabled");
if (mService != null) {
try {
@@ -11984,17 +11998,18 @@
* must handle this intent.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
- * @param timeoutMs Maximum time the profile is allowed to be off in milliseconds or 0 if
- * not limited.
+ * @param timeoutMillis Maximum time the profile is allowed to be off in milliseconds or 0 if
+ * not limited. The minimum non-zero value corresponds to 72 hours. If an admin sets a
+ * smaller non-zero vaulue, 72 hours will be set instead.
* @throws IllegalStateException if the profile owner doesn't have an activity that handles
* {@link #ACTION_CHECK_POLICY_COMPLIANCE}
* @see #setPersonalAppsSuspended
*/
- public void setManagedProfileMaximumTimeOff(@NonNull ComponentName admin, long timeoutMs) {
+ public void setManagedProfileMaximumTimeOff(@NonNull ComponentName admin, long timeoutMillis) {
throwIfParentInstance("setManagedProfileMaximumTimeOff");
if (mService != null) {
try {
- mService.setManagedProfileMaximumTimeOff(admin, timeoutMs);
+ mService.setManagedProfileMaximumTimeOff(admin, timeoutMillis);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index da48663..514677e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -308,11 +308,11 @@
void setAutoTimeRequired(in ComponentName who, boolean required);
boolean getAutoTimeRequired();
- void setAutoTime(in ComponentName who, boolean enabled);
- boolean getAutoTime(in ComponentName who);
+ void setAutoTimeEnabled(in ComponentName who, boolean enabled);
+ boolean getAutoTimeEnabled(in ComponentName who);
- void setAutoTimeZone(in ComponentName who, boolean enabled);
- boolean getAutoTimeZone(in ComponentName who);
+ void setAutoTimeZoneEnabled(in ComponentName who, boolean enabled);
+ boolean getAutoTimeZoneEnabled(in ComponentName who);
void setForceEphemeralUsers(in ComponentName who, boolean forceEpehemeralUsers);
boolean getForceEphemeralUsers(in ComponentName who);
@@ -467,9 +467,9 @@
boolean setKeyGrantForApp(in ComponentName admin, String callerPackage, String alias, String packageName, boolean hasGrant);
- void setProtectedPackages(in ComponentName admin, in List<String> packages);
+ void setUserControlDisabledPackages(in ComponentName admin, in List<String> packages);
- List<String> getProtectedPackages(in ComponentName admin);
+ List<String> getUserControlDisabledPackages(in ComponentName admin);
void setCommonCriteriaModeEnabled(in ComponentName admin, boolean enabled);
boolean isCommonCriteriaModeEnabled(in ComponentName admin);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index db4f1de..917eeb8 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -636,7 +636,6 @@
* @hide
*/
@Nullable
- @SystemApi
public String getDefaultSmsPackage(@UserIdInt int userId) {
try {
return mService.getDefaultSmsPackage(userId);
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 5b98188..d6e7762 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -29,10 +29,10 @@
import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
import android.net.NetworkIdentity;
+import android.net.NetworkStack;
import android.net.NetworkTemplate;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProviderWrapper;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
@@ -527,32 +527,53 @@
/**
* Registers a custom provider of {@link android.net.NetworkStats} to provide network statistics
- * to the system. To unregister, invoke {@link NetworkStatsProviderCallback#unregister()}.
+ * to the system. To unregister, invoke {@link #unregisterNetworkStatsProvider}.
* Note that no de-duplication of statistics between providers is performed, so each provider
- * must only report network traffic that is not being reported by any other provider.
+ * must only report network traffic that is not being reported by any other provider. Also note
+ * that the provider cannot be re-registered after unregistering.
*
* @param tag a human readable identifier of the custom network stats provider. This is only
* used for debugging.
- * @param provider the subclass of {@link AbstractNetworkStatsProvider} that needs to be
+ * @param provider the subclass of {@link NetworkStatsProvider} that needs to be
* registered to the system.
- * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the
- * system or unregister the provider.
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- @NonNull public NetworkStatsProviderCallback registerNetworkStatsProvider(
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STATS_PROVIDER,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+ @NonNull public void registerNetworkStatsProvider(
@NonNull String tag,
- @NonNull AbstractNetworkStatsProvider provider) {
+ @NonNull NetworkStatsProvider provider) {
try {
- final NetworkStatsProviderWrapper wrapper = new NetworkStatsProviderWrapper(provider);
- return new NetworkStatsProviderCallback(
- mService.registerNetworkStatsProvider(tag, wrapper));
+ if (provider.getProviderCallbackBinder() != null) {
+ throw new IllegalArgumentException("provider is already registered");
+ }
+ final INetworkStatsProviderCallback cbBinder =
+ mService.registerNetworkStatsProvider(tag, provider.getProviderBinder());
+ provider.setProviderCallbackBinder(cbBinder);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
- // Unreachable code, but compiler doesn't know about it.
- return null;
+ }
+
+ /**
+ * Unregisters an instance of {@link NetworkStatsProvider}.
+ *
+ * @param provider the subclass of {@link NetworkStatsProvider} that needs to be
+ * unregistered to the system.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STATS_PROVIDER,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+ @NonNull public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
+ try {
+ provider.getProviderCallbackBinderOrThrow().unregister();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
}
private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2c701b4..0a67802 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -204,6 +204,16 @@
/** @hide */
public static final int REASON_SUB_MASK = 0x00FF;
/**
+ * The reason for using the default main reason is unknown or undefined.
+ * @hide
+ */
+ public static final int REASON_SUB_DEFAULT_UNDEFINED = 0x0000;
+ /**
+ * The app was updated.
+ * @hide
+ */
+ public static final int REASON_SUB_DEFAULT_APP_UPDATE = 0x0001;
+ /**
* The app was interacted with in some way by the system.
* @hide
*/
@@ -288,25 +298,25 @@
*/
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
/**
- * The reason for restricting the app is unknown or undefined.
+ * The reason the system forced the app into the bucket is unknown or undefined.
* @hide
*/
- public static final int REASON_SUB_RESTRICT_UNDEFINED = 0x0000;
+ public static final int REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED = 0;
/**
* The app was unnecessarily using system resources (battery, memory, etc) in the background.
* @hide
*/
- public static final int REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE = 0x0001;
+ public static final int REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE = 1 << 0;
/**
* The app was deemed to be intentionally abusive.
* @hide
*/
- public static final int REASON_SUB_RESTRICT_ABUSE = 0x0002;
+ public static final int REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE = 1 << 1;
/**
* The app was displaying buggy behavior.
* @hide
*/
- public static final int REASON_SUB_RESTRICT_BUGGY = 0x0003;
+ public static final int REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY = 1 << 2;
/** @hide */
@@ -322,6 +332,17 @@
@Retention(RetentionPolicy.SOURCE)
public @interface StandbyBuckets {}
+ /** @hide */
+ @IntDef(flag = true, prefix = {"REASON_SUB_FORCED_SYSTEM_FLAG_FLAG_"}, value = {
+ REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED,
+ REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+ REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
+ REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemForcedReasons {
+ }
+
/**
* Observer id of the registered observer for the group of packages that reached the usage
* time limit. Included as an extra in the PendingIntent that was registered.
@@ -1053,26 +1074,24 @@
/** @hide */
public static String reasonToString(int standbyReason) {
+ final int subReason = standbyReason & REASON_SUB_MASK;
StringBuilder sb = new StringBuilder();
switch (standbyReason & REASON_MAIN_MASK) {
case REASON_MAIN_DEFAULT:
sb.append("d");
+ switch (subReason) {
+ case REASON_SUB_DEFAULT_UNDEFINED:
+ // Historically, undefined didn't have a string, so don't add anything here.
+ break;
+ case REASON_SUB_DEFAULT_APP_UPDATE:
+ sb.append("-au");
+ break;
+ }
break;
case REASON_MAIN_FORCED_BY_SYSTEM:
sb.append("s");
- switch (standbyReason & REASON_SUB_MASK) {
- case REASON_SUB_RESTRICT_ABUSE:
- sb.append("-ra");
- break;
- case REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE:
- sb.append("-rbru");
- break;
- case REASON_SUB_RESTRICT_BUGGY:
- sb.append("-rb");
- break;
- case REASON_SUB_RESTRICT_UNDEFINED:
- sb.append("-r");
- break;
+ if (subReason > 0) {
+ sb.append("-").append(Integer.toBinaryString(subReason));
}
break;
case REASON_MAIN_FORCED_BY_USER:
@@ -1080,7 +1099,7 @@
break;
case REASON_MAIN_PREDICTED:
sb.append("p");
- switch (standbyReason & REASON_SUB_MASK) {
+ switch (subReason) {
case REASON_SUB_PREDICTED_RESTORED:
sb.append("-r");
break;
@@ -1091,7 +1110,7 @@
break;
case REASON_MAIN_USAGE:
sb.append("u");
- switch (standbyReason & REASON_SUB_MASK) {
+ switch (subReason) {
case REASON_SUB_USAGE_SYSTEM_INTERACTION:
sb.append("-si");
break;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 6ae68fc..608b563 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -849,7 +849,7 @@
synchronized (mLock) {
if (sBluetoothLeScanner == null) {
sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
}
}
return sBluetoothLeScanner;
@@ -1663,11 +1663,11 @@
return ActivityThread.currentOpPackageName();
}
- private String getFeatureId() {
+ private String getAttributionTag() {
// Workaround for legacy API for getting a BluetoothAdapter not
// passing a context
if (mContext != null) {
- return mContext.getFeatureId();
+ return mContext.getAttributionTag();
}
return null;
}
@@ -1709,7 +1709,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.startDiscovery(getOpPackageName(), getFeatureId());
+ return mService.startDiscovery(getOpPackageName(), getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 7ff6466..3b4fe0a 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -62,7 +62,7 @@
* @hide
*/
public BluetoothManager(Context context) {
- if (context.getFeatureId() == null) {
+ if (context.getAttributionTag() == null) {
context = context.getApplicationContext();
if (context == null) {
throw new IllegalArgumentException(
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index d4b5b1a..4bd7b05 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -257,11 +257,22 @@
/**
* Check if a given package was {@link #associate associated} with a device with given
- * mac address by given user.
+ * Wi-Fi MAC address for a given user.
*
- * @param packageName the package to check for
- * @param macAddress the mac address or BSSID of the device to check for
- * @param user the user to check for
+ * <p>This is a system API protected by the
+ * {@link andrioid.Manifest.permission#MANAGE_COMPANION_DEVICES} permission, that’s currently
+ * called by the Android Wi-Fi stack to determine whether user consent is required to connect
+ * to a Wi-Fi network. Devices that have been pre-registered as companion devices will not
+ * require user consent to connect.</p>
+ *
+ * <p>Note if the caller has the
+ * {@link android.Manifest.permission#COMPANION_APPROVE_WIFI_CONNECTIONS} permission, this
+ * method will return true by default.</p>
+ *
+ * @param packageName the name of the package that has the association with the companion device
+ * @param macAddress the Wi-Fi MAC address or BSSID of the companion device to check for
+ * @param user the user handle that currently hosts the package being queried for a companion
+ * device association
* @return whether a corresponding association record exists
*
* @hide
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index bd3298c..d8e8b27 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -230,12 +230,12 @@
}
@Override
- public Cursor query(String callingPkg, @Nullable String featureId, Uri uri,
+ public Cursor query(String callingPkg, @Nullable String attributionTag, Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, featureId, uri, null)
+ if (enforceReadPermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
// The caller has no access to the data, so return an empty cursor with
// the columns in the requested order. The caller may ask for an invalid
@@ -253,7 +253,7 @@
// columns. We then use the column names to return an empty cursor.
Cursor cursor;
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
cursor = mInterface.query(
uri, projection, queryArgs,
@@ -272,7 +272,7 @@
}
Trace.traceBegin(TRACE_TAG_DATABASE, "query");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.query(
uri, projection, queryArgs,
@@ -308,15 +308,15 @@
}
@Override
- public Uri insert(String callingPkg, @Nullable String featureId, Uri uri,
+ public Uri insert(String callingPkg, @Nullable String attributionTag, Uri uri,
ContentValues initialValues, Bundle extras) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, featureId, uri, null)
+ if (enforceWritePermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return rejectInsert(uri, initialValues);
} finally {
@@ -325,7 +325,7 @@
}
Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return maybeAddUserId(mInterface.insert(uri, initialValues, extras), userId);
} catch (RemoteException e) {
@@ -337,17 +337,17 @@
}
@Override
- public int bulkInsert(String callingPkg, @Nullable String featureId, Uri uri,
+ public int bulkInsert(String callingPkg, @Nullable String attributionTag, Uri uri,
ContentValues[] initialValues) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, featureId, uri, null)
+ if (enforceWritePermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.bulkInsert(uri, initialValues);
} catch (RemoteException e) {
@@ -359,8 +359,9 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
- String authority, ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(String callingPkg,
+ @Nullable String attributionTag, String authority,
+ ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
validateIncomingAuthority(authority);
int numOperations = operations.size();
@@ -377,13 +378,13 @@
operations.set(i, operation);
}
if (operation.isReadOperation()) {
- if (enforceReadPermission(callingPkg, featureId, uri, null)
+ if (enforceReadPermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
if (operation.isWriteOperation()) {
- if (enforceWritePermission(callingPkg, featureId, uri, null)
+ if (enforceWritePermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
@@ -391,7 +392,7 @@
}
Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
ContentProviderResult[] results = mInterface.applyBatch(authority,
operations);
@@ -413,16 +414,17 @@
}
@Override
- public int delete(String callingPkg, @Nullable String featureId, Uri uri, Bundle extras) {
+ public int delete(String callingPkg, @Nullable String attributionTag, Uri uri,
+ Bundle extras) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, featureId, uri, null)
+ if (enforceWritePermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.delete(uri, extras);
} catch (RemoteException e) {
@@ -434,17 +436,17 @@
}
@Override
- public int update(String callingPkg, @Nullable String featureId, Uri uri,
+ public int update(String callingPkg, @Nullable String attributionTag, Uri uri,
ContentValues values, Bundle extras) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, featureId, uri, null)
+ if (enforceWritePermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "update");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.update(uri, values, extras);
} catch (RemoteException e) {
@@ -456,15 +458,15 @@
}
@Override
- public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId,
+ public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken)
throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, featureId, uri, mode, callerToken);
+ enforceFilePermission(callingPkg, attributionTag, uri, mode, callerToken);
Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.openFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
@@ -477,15 +479,15 @@
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, featureId, uri, mode, null);
+ enforceFilePermission(callingPkg, attributionTag, uri, mode, null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.openAssetFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
@@ -498,13 +500,13 @@
}
@Override
- public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
String method, @Nullable String arg, @Nullable Bundle extras) {
validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
Trace.traceBegin(TRACE_TAG_DATABASE, "call");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.call(authority, method, arg, extras);
} catch (RemoteException e) {
@@ -532,15 +534,15 @@
@Override
public AssetFileDescriptor openTypedAssetFile(String callingPkg,
- @Nullable String featureId, Uri uri, String mimeType, Bundle opts,
+ @Nullable String attributionTag, Uri uri, String mimeType, Bundle opts,
ICancellationSignal cancellationSignal) throws FileNotFoundException {
Bundle.setDefusable(opts, true);
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, featureId, uri, "r", null);
+ enforceFilePermission(callingPkg, attributionTag, uri, "r", null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.openTypedAssetFile(
uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
@@ -558,17 +560,17 @@
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
+ public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, featureId, uri, null)
+ if (enforceReadPermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return maybeAddUserId(mInterface.canonicalize(uri), userId);
} catch (RemoteException e) {
@@ -580,26 +582,26 @@
}
@Override
- public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+ public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
RemoteCallback callback) {
final Bundle result = new Bundle();
result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- canonicalize(callingPkg, featureId, uri));
+ canonicalize(callingPkg, attributionTag, uri));
callback.sendResult(result);
}
@Override
- public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) {
+ public Uri uncanonicalize(String callingPkg, String attributionTag, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, featureId, uri, null)
+ if (enforceReadPermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return maybeAddUserId(mInterface.uncanonicalize(uri), userId);
} catch (RemoteException e) {
@@ -611,17 +613,17 @@
}
@Override
- public boolean refresh(String callingPkg, String featureId, Uri uri, Bundle extras,
+ public boolean refresh(String callingPkg, String attributionTag, Uri uri, Bundle extras,
ICancellationSignal cancellationSignal) throws RemoteException {
uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, featureId, uri, null)
+ if (enforceReadPermission(callingPkg, attributionTag, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
return false;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.refresh(uri, extras,
CancellationSignal.fromTransport(cancellationSignal));
@@ -632,13 +634,13 @@
}
@Override
- public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
int uid, int modeFlags) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, featureId));
+ new Pair<>(callingPkg, attributionTag));
try {
return mInterface.checkUriPermission(uri, uid, modeFlags);
} catch (RemoteException e) {
@@ -649,47 +651,50 @@
}
}
- private void enforceFilePermission(String callingPkg, @Nullable String featureId, Uri uri,
- String mode, IBinder callerToken) throws FileNotFoundException, SecurityException {
+ private void enforceFilePermission(String callingPkg, @Nullable String attributionTag,
+ Uri uri, String mode, IBinder callerToken)
+ throws FileNotFoundException, SecurityException {
if (mode != null && mode.indexOf('w') != -1) {
- if (enforceWritePermission(callingPkg, featureId, uri, callerToken)
+ if (enforceWritePermission(callingPkg, attributionTag, uri, callerToken)
!= AppOpsManager.MODE_ALLOWED) {
throw new FileNotFoundException("App op not allowed");
}
} else {
- if (enforceReadPermission(callingPkg, featureId, uri, callerToken)
+ if (enforceReadPermission(callingPkg, attributionTag, uri, callerToken)
!= AppOpsManager.MODE_ALLOWED) {
throw new FileNotFoundException("App op not allowed");
}
}
}
- private int enforceReadPermission(String callingPkg, @Nullable String featureId, Uri uri,
- IBinder callerToken)
+ private int enforceReadPermission(String callingPkg, @Nullable String attributionTag,
+ Uri uri, IBinder callerToken)
throws SecurityException {
- final int mode = enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
+ final int mode = enforceReadPermissionInner(uri, callingPkg, attributionTag,
+ callerToken);
if (mode != MODE_ALLOWED) {
return mode;
}
- return noteProxyOp(callingPkg, featureId, mReadOp);
+ return noteProxyOp(callingPkg, attributionTag, mReadOp);
}
- private int enforceWritePermission(String callingPkg, String featureId, Uri uri,
+ private int enforceWritePermission(String callingPkg, String attributionTag, Uri uri,
IBinder callerToken)
throws SecurityException {
- final int mode = enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
+ final int mode = enforceWritePermissionInner(uri, callingPkg, attributionTag,
+ callerToken);
if (mode != MODE_ALLOWED) {
return mode;
}
- return noteProxyOp(callingPkg, featureId, mWriteOp);
+ return noteProxyOp(callingPkg, attributionTag, mWriteOp);
}
- private int noteProxyOp(String callingPkg, String featureId, int op) {
+ private int noteProxyOp(String callingPkg, String attributionTag, int op) {
if (op != AppOpsManager.OP_NONE) {
int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(),
- featureId, null);
+ attributionTag, null);
return mode == MODE_DEFAULT ? MODE_IGNORED : mode;
}
@@ -711,19 +716,19 @@
* associated with that permission.
*/
private int checkPermissionAndAppOp(String permission, String callingPkg,
- @Nullable String featureId, IBinder callerToken) {
+ @Nullable String attributionTag, IBinder callerToken) {
if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
callerToken) != PERMISSION_GRANTED) {
return MODE_ERRORED;
}
- return mTransport.noteProxyOp(callingPkg, featureId,
+ return mTransport.noteProxyOp(callingPkg, attributionTag,
AppOpsManager.permissionToOpCode(permission));
}
/** {@hide} */
protected int enforceReadPermissionInner(Uri uri, String callingPkg,
- @Nullable String featureId, IBinder callerToken) throws SecurityException {
+ @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -737,7 +742,7 @@
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getReadPermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId,
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, attributionTag,
callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
@@ -757,8 +762,8 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getReadPermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId,
- callerToken);
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
+ attributionTag, callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -807,7 +812,7 @@
/** {@hide} */
protected int enforceWritePermissionInner(Uri uri, String callingPkg,
- @Nullable String featureId, IBinder callerToken) throws SecurityException {
+ @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -821,8 +826,8 @@
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getWritePermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId,
- callerToken);
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg,
+ attributionTag, callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -841,8 +846,8 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getWritePermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId,
- callerToken);
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
+ attributionTag, callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -943,16 +948,16 @@
}
/**
- * Return the feature in the package of the caller that initiated the request being
+ * Return the attribution tag of the caller that initiated the request being
* processed on the current thread. Returns {@code null} if not currently processing
- * a request of the request is for the default feature.
+ * a request of the request is for the default attribution.
* <p>
* This will always return {@code null} when processing
* {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
*
* @see #getCallingPackage
*/
- public final @Nullable String getCallingFeatureId() {
+ public final @Nullable String getCallingAttributionTag() {
final Pair<String, String> pkg = mCallingPackage.get();
if (pkg != null) {
return pkg.second;
@@ -962,6 +967,14 @@
}
/**
+ * @removed
+ */
+ @Deprecated
+ public final @Nullable String getCallingFeatureId() {
+ return getCallingAttributionTag();
+ }
+
+ /**
* Return the package name of the caller that initiated the request being
* processed on the current thread. The returned package will have
* <em>not</em> been verified to belong to the calling UID. Returns
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index a9b7862..d0f5ec4 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -80,7 +80,7 @@
private final IContentProvider mContentProvider;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mPackageName;
- private final @Nullable String mFeatureId;
+ private final @Nullable String mAttributionTag;
private final String mAuthority;
private final boolean mStable;
@@ -104,7 +104,7 @@
mContentResolver = contentResolver;
mContentProvider = contentProvider;
mPackageName = contentResolver.mPackageName;
- mFeatureId = contentResolver.mFeatureId;
+ mAttributionTag = contentResolver.mAttributionTag;
mAuthority = authority;
mStable = stable;
@@ -195,7 +195,8 @@
cancellationSignal.setRemote(remoteCancellationSignal);
}
final Cursor cursor = mContentProvider.query(
- mPackageName, mFeatureId, uri, projection, queryArgs, remoteCancellationSignal);
+ mPackageName, mAttributionTag, uri, projection, queryArgs,
+ remoteCancellationSignal);
if (cursor == null) {
return null;
}
@@ -255,7 +256,7 @@
beforeRemote();
try {
- return mContentProvider.canonicalize(mPackageName, mFeatureId, url);
+ return mContentProvider.canonicalize(mPackageName, mAttributionTag, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -273,7 +274,7 @@
beforeRemote();
try {
- return mContentProvider.uncanonicalize(mPackageName, mFeatureId, url);
+ return mContentProvider.uncanonicalize(mPackageName, mAttributionTag, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -298,7 +299,7 @@
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return mContentProvider.refresh(mPackageName, mFeatureId, url, extras,
+ return mContentProvider.refresh(mPackageName, mAttributionTag, url, extras,
remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -318,7 +319,7 @@
beforeRemote();
try {
- return mContentProvider.checkUriPermission(mPackageName, mFeatureId, uri, uid,
+ return mContentProvider.checkUriPermission(mPackageName, mAttributionTag, uri, uid,
modeFlags);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -344,7 +345,8 @@
beforeRemote();
try {
- return mContentProvider.insert(mPackageName, mFeatureId, url, initialValues, extras);
+ return mContentProvider.insert(mPackageName, mAttributionTag, url, initialValues,
+ extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -364,7 +366,7 @@
beforeRemote();
try {
- return mContentProvider.bulkInsert(mPackageName, mFeatureId, url, initialValues);
+ return mContentProvider.bulkInsert(mPackageName, mAttributionTag, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -388,7 +390,7 @@
beforeRemote();
try {
- return mContentProvider.delete(mPackageName, mFeatureId, url, extras);
+ return mContentProvider.delete(mPackageName, mAttributionTag, url, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -413,7 +415,7 @@
beforeRemote();
try {
- return mContentProvider.update(mPackageName, mFeatureId, url, values, extras);
+ return mContentProvider.update(mPackageName, mAttributionTag, url, values, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -457,8 +459,8 @@
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openFile(mPackageName, mFeatureId, url, mode, remoteSignal,
- null);
+ return mContentProvider.openFile(mPackageName, mAttributionTag, url, mode,
+ remoteSignal, null);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -502,7 +504,7 @@
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openAssetFile(mPackageName, mFeatureId, url, mode,
+ return mContentProvider.openAssetFile(mPackageName, mAttributionTag, url, mode,
remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -544,7 +546,7 @@
signal.setRemote(remoteSignal);
}
return mContentProvider.openTypedAssetFile(
- mPackageName, mFeatureId, uri, mimeTypeFilter, opts, remoteSignal);
+ mPackageName, mAttributionTag, uri, mimeTypeFilter, opts, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -571,7 +573,8 @@
beforeRemote();
try {
- return mContentProvider.applyBatch(mPackageName, mFeatureId, authority, operations);
+ return mContentProvider.applyBatch(mPackageName, mAttributionTag, authority,
+ operations);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -597,7 +600,8 @@
beforeRemote();
try {
- return mContentProvider.call(mPackageName, mFeatureId, authority, method, arg, extras);
+ return mContentProvider.call(mPackageName, mAttributionTag, authority, method, arg,
+ extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 31e1fc8..b134c37 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -767,7 +767,7 @@
public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) {
mContext = context != null ? context : ActivityThread.currentApplication();
mPackageName = mContext.getOpPackageName();
- mFeatureId = mContext.getFeatureId();
+ mAttributionTag = mContext.getAttributionTag();
mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
mWrapped = wrapped;
}
@@ -1144,7 +1144,7 @@
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
- qCursor = unstableProvider.query(mPackageName, mFeatureId, uri, projection,
+ qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
@@ -1155,7 +1155,7 @@
if (stableProvider == null) {
return null;
}
- qCursor = stableProvider.query(mPackageName, mFeatureId, uri, projection,
+ qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
@@ -1247,7 +1247,7 @@
try {
final UriResultListener resultListener = new UriResultListener();
- provider.canonicalizeAsync(mPackageName, mFeatureId, url,
+ provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
new RemoteCallback(resultListener));
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
return resultListener.result;
@@ -1294,7 +1294,7 @@
}
try {
- return provider.uncanonicalize(mPackageName, mFeatureId, url);
+ return provider.uncanonicalize(mPackageName, mAttributionTag, url);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -1346,7 +1346,7 @@
remoteCancellationSignal = provider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return provider.refresh(mPackageName, mFeatureId, url, extras,
+ return provider.refresh(mPackageName, mAttributionTag, url, extras,
remoteCancellationSignal);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
@@ -1748,7 +1748,8 @@
try {
fd = unstableProvider.openAssetFile(
- mPackageName, mFeatureId, uri, mode, remoteCancellationSignal);
+ mPackageName, mAttributionTag, uri, mode,
+ remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1763,7 +1764,7 @@
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openAssetFile(
- mPackageName, mFeatureId, uri, mode, remoteCancellationSignal);
+ mPackageName, mAttributionTag, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1914,7 +1915,8 @@
try {
fd = unstableProvider.openTypedAssetFile(
- mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal);
+ mPackageName, mAttributionTag, uri, mimeType, opts,
+ remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1929,7 +1931,8 @@
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openTypedAssetFile(
- mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal);
+ mPackageName, mAttributionTag, uri, mimeType, opts,
+ remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -2077,7 +2080,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- Uri createdRow = provider.insert(mPackageName, mFeatureId, url, values, extras);
+ Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
@@ -2158,7 +2161,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsCreated = provider.bulkInsert(mPackageName, mFeatureId, url, values);
+ int rowsCreated = provider.bulkInsert(mPackageName, mAttributionTag, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
return rowsCreated;
@@ -2217,7 +2220,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsDeleted = provider.delete(mPackageName, mFeatureId, url, extras);
+ int rowsDeleted = provider.delete(mPackageName, mAttributionTag, url, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "delete", null);
return rowsDeleted;
@@ -2284,7 +2287,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsUpdated = provider.update(mPackageName, mFeatureId, uri, values, extras);
+ int rowsUpdated = provider.update(mPackageName, mAttributionTag, uri, values, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, uri, "update", null);
return rowsUpdated;
@@ -2333,7 +2336,7 @@
throw new IllegalArgumentException("Unknown authority " + authority);
}
try {
- final Bundle res = provider.call(mPackageName, mFeatureId, authority, method, arg,
+ final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,
extras);
Bundle.setDefusable(res, true);
return res;
@@ -3746,8 +3749,8 @@
}
/** @hide */
- public @Nullable String getFeatureId() {
- return mFeatureId;
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
}
@UnsupportedAppUsage
@@ -3757,7 +3760,7 @@
@UnsupportedAppUsage
final String mPackageName;
- final @Nullable String mFeatureId;
+ final @Nullable String mAttributionTag;
final int mTargetSdkVersion;
final ContentInterface mWrapped;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6cba327..318ae11 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -71,6 +71,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
import android.view.autofill.AutofillManager.AutofillClient;
import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.view.textclassifier.TextClassificationManager;
@@ -814,16 +815,25 @@
}
/**
- * <p>Features are used in complex apps to logically separate parts of the app. E.g. a
- * blogging app might also have a instant messaging app built in.
+ * <p>Attribution can be used in complex apps to logically separate parts of the app. E.g. a
+ * blogging app might also have a instant messaging app built in. In this case two separate tags
+ * can for used each sub-feature.
*
- * @return the feature id this context is for or {@code null} if this is the default
- * feature.
+ * @return the attribution tag this context is for or {@code null} if this is the default.
*/
- public @Nullable String getFeatureId() {
+ public @Nullable String getAttributionTag() {
return null;
}
+ // TODO moltmann: Remove
+ /**
+ * @removed
+ */
+ @Deprecated
+ public @Nullable String getFeatureId() {
+ return getAttributionTag();
+ }
+
/** Return the full application info for this context's package. */
public abstract ApplicationInfo getApplicationInfo();
@@ -5820,24 +5830,34 @@
* @see #WALLPAPER_SERVICE
* @throws IllegalArgumentException if token is invalid
*/
- public @NonNull Context createWindowContext(int type, @Nullable Bundle options) {
+ public @NonNull Context createWindowContext(@WindowType int type, @Nullable Bundle options) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
/**
- * Return a new Context object for the current Context but for a different feature in the app.
- * Features can be used by complex apps to separate logical parts.
+ * Return a new Context object for the current Context but attribute to a different tag.
+ * In complex apps attribution tagging can be used to distinguish between separate logical
+ * parts.
*
- * @param featureId The feature id or {@code null} to create a context for the default feature.
+ * @param attributionTag The tag or {@code null} to create a context for the default.
*
- * @return A {@link Context} for the feature
+ * @return A {@link Context} that is tagged for the new attribution
*
- * @see #getFeatureId()
+ * @see #getAttributionTag()
*/
- public @NonNull Context createFeatureContext(@Nullable String featureId) {
+ public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
+ // TODO moltmann: remove
+ /**
+ * @removed
+ */
+ @Deprecated
+ public @NonNull Context createFeatureContext(@Nullable String featureId) {
+ return createAttributionContext(featureId);
+ }
+
/**
* Return a new Context object for the current Context but whose storage
* APIs are backed by device-protected storage.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 91d214b..d389d2a 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -42,6 +42,7 @@
import android.os.UserHandle;
import android.view.Display;
import android.view.DisplayAdjustments;
+import android.view.WindowManager.LayoutParams.WindowType;
import android.view.autofill.AutofillManager.AutofillClient;
import java.io.File;
@@ -163,8 +164,8 @@
/** @hide */
@Override
- public @Nullable String getFeatureId() {
- return mBase.getFeatureId();
+ public @Nullable String getAttributionTag() {
+ return mBase.getAttributionTag();
}
@Override
@@ -978,13 +979,13 @@
@Override
@NonNull
- public Context createWindowContext(int type, @Nullable Bundle options) {
+ public Context createWindowContext(@WindowType int type, @Nullable Bundle options) {
return mBase.createWindowContext(type, options);
}
@Override
- public @NonNull Context createFeatureContext(@Nullable String featureId) {
- return mBase.createFeatureContext(featureId);
+ public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
+ return mBase.createAttributionContext(attributionTag);
}
@Override
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 37643da..84b0f0e 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -38,7 +38,7 @@
* @hide
*/
public interface IContentProvider extends IInterface {
- public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+ public Cursor query(String callingPkg, @Nullable String attributionTag, Uri url,
@Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
@@ -59,8 +59,8 @@
throws RemoteException {
return insert(callingPkg, null, url, initialValues, null);
}
- public Uri insert(String callingPkg, String featureId, Uri url, ContentValues initialValues,
- Bundle extras) throws RemoteException;
+ public Uri insert(String callingPkg, String attributionTag, Uri url,
+ ContentValues initialValues, Bundle extras) throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])"
@@ -69,7 +69,7 @@
throws RemoteException {
return bulkInsert(callingPkg, null, url, initialValues);
}
- public int bulkInsert(String callingPkg, String featureId, Uri url,
+ public int bulkInsert(String callingPkg, String attributionTag, Uri url,
ContentValues[] initialValues) throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
@@ -80,7 +80,7 @@
return delete(callingPkg, null, url,
ContentResolver.createSqlQueryBundle(selection, selectionArgs));
}
- public int delete(String callingPkg, String featureId, Uri url, Bundle extras)
+ public int delete(String callingPkg, String attributionTag, Uri url, Bundle extras)
throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
@@ -91,18 +91,18 @@
return update(callingPkg, null, url, values,
ContentResolver.createSqlQueryBundle(selection, selectionArgs));
}
- public int update(String callingPkg, String featureId, Uri url, ContentValues values,
+ public int update(String callingPkg, String attributionTag, Uri url, ContentValues values,
Bundle extras) throws RemoteException;
- public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
- String mode, ICancellationSignal signal, IBinder callerToken)
+ public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
+ Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
throws RemoteException, FileNotFoundException;
- public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
- public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String attributionTag,
String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
@@ -115,15 +115,15 @@
return call(callingPkg, null, "unknown", method, arg, extras);
}
- public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
- public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid,
- int modeFlags) throws RemoteException;
+ public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
+ int uid, int modeFlags) throws RemoteException;
public ICancellationSignal createCancellationSignal() throws RemoteException;
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
throws RemoteException;
/**
@@ -131,20 +131,21 @@
* call returns immediately, and the resulting type is returned when available via
* a binder callback.
*/
- void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+ void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
RemoteCallback callback) throws RemoteException;
- public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ public Uri uncanonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
throws RemoteException;
- public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
+ public boolean refresh(String callingPkg, @Nullable String attributionTag, Uri url,
@Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
- Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg,
+ @Nullable String attributionTag, Uri url, String mimeType, Bundle opts,
+ ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
/* IPC constants */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4bb7346..38c1890 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -26,6 +26,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.AppGlobals;
@@ -86,6 +87,7 @@
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
+import java.util.TimeZone;
/**
* An intent is an abstract description of an operation to be performed. It
@@ -2312,7 +2314,8 @@
/**
* Broadcast Action: The timezone has changed. The intent will have the following extra values:</p>
* <ul>
- * <li><em>time-zone</em> - The java.util.TimeZone.getID() value identifying the new time zone.</li>
+ * <li>{@link #EXTRA_TIMEZONE} - The java.util.TimeZone.getID() value identifying the new
+ * time zone.</li>
* </ul>
*
* <p class="note">This is a protected intent that can only be sent
@@ -5785,6 +5788,14 @@
public static final String EXTRA_TIME = "android.intent.extra.TIME";
/**
+ * Extra sent with {@link #ACTION_TIMEZONE_CHANGED} specifying the new time zone of the device.
+ *
+ * <p>Type: String, the same as returned by {@link TimeZone#getID()} to identify time zones.
+ */
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_TIMEZONE = "time-zone";
+
+ /**
* Optional int extra for {@link #ACTION_TIME_CHANGED} that indicates the
* user has set their time format preference. See {@link #EXTRA_TIME_PREF_VALUE_USE_12_HOUR},
* {@link #EXTRA_TIME_PREF_VALUE_USE_24_HOUR} and
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 33bd839..b434072 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -123,7 +123,7 @@
* @param uid The uid for which to check.
* @param packageName The package name for which to check. If null the
* the first package for the calling UID will be used.
- * @param featureId Feature in the package
+ * @param attributionTag attribution tag
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
* @param message A message describing the reason the permission was checked
@@ -133,9 +133,9 @@
@PermissionResult
public static int checkPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, int pid, int uid, @Nullable String packageName,
- @Nullable String featureId, @Nullable String message) {
- return checkPermissionCommon(context, permission, pid, uid, packageName, featureId, message,
- true /*forDataDelivery*/);
+ @Nullable String attributionTag, @Nullable String message) {
+ return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag,
+ message, true /*forDataDelivery*/);
}
/**
@@ -171,8 +171,8 @@
@PermissionResult
public static int checkPermissionForPreflight(@NonNull Context context,
@NonNull String permission, int pid, int uid, @Nullable String packageName) {
- return checkPermissionCommon(context, permission, pid, uid, packageName, null /*featureId*/,
- null /*message*/, false /*forDataDelivery*/);
+ return checkPermissionCommon(context, permission, pid, uid, packageName,
+ null /*attributionTag*/, null /*message*/, false /*forDataDelivery*/);
}
/**
@@ -207,7 +207,7 @@
public static int checkSelfPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, @Nullable String message) {
return checkPermissionForDataDelivery(context, permission, Process.myPid(),
- Process.myUid(), context.getPackageName(), context.getFeatureId(), message);
+ Process.myUid(), context.getPackageName(), context.getAttributionTag(), message);
}
/**
@@ -266,7 +266,7 @@
* @param permission The permission to check.
* @param packageName The package name making the IPC. If null the
* the first package for the calling UID will be used.
- * @param featureId The feature inside of the app
+ * @param attributionTag attribution tag
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
* @param message A message describing the reason the permission was checked
@@ -276,12 +276,12 @@
@PermissionResult
public static int checkCallingPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, @Nullable String packageName,
- @Nullable String featureId, @Nullable String message) {
+ @Nullable String attributionTag, @Nullable String message) {
if (Binder.getCallingPid() == Process.myPid()) {
return PERMISSION_HARD_DENIED;
}
return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
- Binder.getCallingUid(), packageName, featureId, message);
+ Binder.getCallingUid(), packageName, attributionTag, message);
}
/**
@@ -343,20 +343,20 @@
* @param permission The permission to check.
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
- * @param featureId feature Id of caller (if not self)
+ * @param attributionTag attribution tag of caller (if not self)
* @param message A message describing the reason the permission was checked
*
* @see #checkCallingOrSelfPermissionForPreflight(Context, String)
*/
@PermissionResult
public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context,
- @NonNull String permission, @Nullable String featureId, @Nullable String message) {
+ @NonNull String permission, @Nullable String attributionTag, @Nullable String message) {
String packageName = (Binder.getCallingPid() == Process.myPid())
? context.getPackageName() : null;
- featureId = (Binder.getCallingPid() == Process.myPid())
- ? context.getFeatureId() : featureId;
+ attributionTag = (Binder.getCallingPid() == Process.myPid())
+ ? context.getAttributionTag() : attributionTag;
return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
- Binder.getCallingUid(), packageName, featureId, message);
+ Binder.getCallingUid(), packageName, attributionTag, message);
}
/**
@@ -395,7 +395,7 @@
}
static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String featureId,
+ int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
@Nullable String message, boolean forDataDelivery) {
final PermissionInfo permissionInfo;
try {
@@ -414,18 +414,18 @@
}
if (permissionInfo.isAppOp()) {
- return checkAppOpPermission(context, permission, pid, uid, packageName, featureId,
+ return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag,
message, forDataDelivery);
}
if (permissionInfo.isRuntime()) {
- return checkRuntimePermission(context, permission, pid, uid, packageName, featureId,
- message, forDataDelivery);
+ return checkRuntimePermission(context, permission, pid, uid, packageName,
+ attributionTag, message, forDataDelivery);
}
return context.checkPermission(permission, pid, uid);
}
private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String featureId,
+ int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
@Nullable String message, boolean forDataDelivery) {
final String op = AppOpsManager.permissionToOp(permission);
if (op == null || packageName == null) {
@@ -434,11 +434,12 @@
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
final int opMode = (forDataDelivery)
- ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, featureId, message)
- : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName);
+ ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
+ : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
switch (opMode) {
- case AppOpsManager.MODE_ALLOWED: {
+ case AppOpsManager.MODE_ALLOWED:
+ case AppOpsManager.MODE_FOREGROUND: {
return PERMISSION_GRANTED;
}
case AppOpsManager.MODE_DEFAULT: {
@@ -453,7 +454,7 @@
}
private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String featureId,
+ int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
@Nullable String message, boolean forDataDelivery) {
if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
return PERMISSION_HARD_DENIED;
@@ -466,13 +467,15 @@
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
final int opMode = (forDataDelivery)
- ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, featureId, message)
- : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName);
+ ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
+ : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
- if (opMode == AppOpsManager.MODE_ALLOWED) {
- return PERMISSION_GRANTED;
- } else {
- return PERMISSION_SOFT_DENIED;
+ switch (opMode) {
+ case AppOpsManager.MODE_ALLOWED:
+ case AppOpsManager.MODE_FOREGROUND:
+ return PERMISSION_GRANTED;
+ default:
+ return PERMISSION_SOFT_DENIED;
}
}
}
diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java
index 9f95d4d..2869abb 100644
--- a/core/java/android/content/integrity/AppIntegrityManager.java
+++ b/core/java/android/content/integrity/AppIntegrityManager.java
@@ -25,6 +25,8 @@
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
+import java.util.List;
+
/**
* Class for pushing rules used to check the integrity of app installs.
*
@@ -121,4 +123,21 @@
throw e.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Get the package names of all whitelisted rule providers.
+ *
+ * <p>Warning: this method is only used for tests.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public List<String> getWhitelistedRuleProviders() {
+ try {
+ return mManager.getWhitelistedRuleProviders();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/core/java/android/content/integrity/IAppIntegrityManager.aidl b/core/java/android/content/integrity/IAppIntegrityManager.aidl
index 4714ad7..94197bb 100644
--- a/core/java/android/content/integrity/IAppIntegrityManager.aidl
+++ b/core/java/android/content/integrity/IAppIntegrityManager.aidl
@@ -19,6 +19,7 @@
import android.content.integrity.Rule;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
+import java.util.List;
/** @hide */
interface IAppIntegrityManager {
@@ -26,4 +27,5 @@
String getCurrentRuleSetVersion();
String getCurrentRuleSetProvider();
ParceledListSlice<Rule> getCurrentRules();
+ List<String> getWhitelistedRuleProviders();
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 0b2b5b1..f25ce76 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1222,13 +1222,7 @@
dest.writeInt(lockTaskLaunchMode);
if (windowLayout != null) {
dest.writeInt(1);
- dest.writeInt(windowLayout.width);
- dest.writeFloat(windowLayout.widthFraction);
- dest.writeInt(windowLayout.height);
- dest.writeFloat(windowLayout.heightFraction);
- dest.writeInt(windowLayout.gravity);
- dest.writeInt(windowLayout.minWidth);
- dest.writeInt(windowLayout.minHeight);
+ windowLayout.writeToParcel(dest);
} else {
dest.writeInt(0);
}
@@ -1372,8 +1366,8 @@
* @attr ref android.R.styleable#AndroidManifestLayout_minHeight
*/
public static final class WindowLayout {
- public WindowLayout(int width, float widthFraction, int height, float heightFraction, int gravity,
- int minWidth, int minHeight) {
+ public WindowLayout(int width, float widthFraction, int height, float heightFraction,
+ int gravity, int minWidth, int minHeight) {
this.width = width;
this.widthFraction = widthFraction;
this.height = height;
@@ -1392,6 +1386,7 @@
gravity = source.readInt();
minWidth = source.readInt();
minHeight = source.readInt();
+ windowLayoutAffinity = source.readString();
}
/**
@@ -1458,11 +1453,30 @@
public final int minHeight;
/**
+ * Affinity of window layout parameters. Activities with the same UID and window layout
+ * affinity will share the same window dimension record.
+ * @hide
+ */
+ public String windowLayoutAffinity;
+
+ /**
* Returns if this {@link WindowLayout} has specified bounds.
* @hide
*/
public boolean hasSpecifiedSize() {
return width >= 0 || height >= 0 || widthFraction >= 0 || heightFraction >= 0;
}
+
+ /** @hide */
+ public void writeToParcel(Parcel dest) {
+ dest.writeInt(width);
+ dest.writeFloat(widthFraction);
+ dest.writeInt(height);
+ dest.writeFloat(heightFraction);
+ dest.writeInt(gravity);
+ dest.writeInt(minWidth);
+ dest.writeInt(minHeight);
+ dest.writeString(windowLayoutAffinity);
+ }
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index a15afe0..c82fffa 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -25,6 +25,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProcessInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -38,6 +39,8 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
import com.android.server.SystemConfig;
import java.lang.annotation.Retention;
@@ -56,7 +59,8 @@
* <application> tag.
*/
public class ApplicationInfo extends PackageItemInfo implements Parcelable {
-
+ private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+
/**
* Default task affinity of all activities in this application. See
* {@link ActivityInfo#taskAffinity} for more information. This comes
@@ -1273,6 +1277,14 @@
public String zygotePreloadName;
/**
+ * Indicates if the application has requested GWP-ASan to be enabled, disabled, or left
+ * unspecified. Processes can override this setting.
+ * @hide
+ */
+ @Nullable
+ public Boolean enableGwpAsan;
+
+ /**
* Represents the default policy. The actual policy used will depend on other properties of
* the application, e.g. the target SDK version.
* @hide
@@ -1413,6 +1425,9 @@
pw.println(prefix + "usesNonSdkApi=" + usesNonSdkApi());
pw.println(prefix + "allowsPlaybackCapture="
+ (isAudioPlaybackCaptureAllowed() ? "true" : "false"));
+ if (enableGwpAsan != null) {
+ pw.println(prefix + "enableGwpAsan=" + enableGwpAsan);
+ }
}
super.dumpBack(pw, prefix);
}
@@ -1511,6 +1526,9 @@
if (category != CATEGORY_UNDEFINED) {
proto.write(ApplicationInfoProto.Detail.CATEGORY, category);
}
+ if (enableGwpAsan != null) {
+ proto.write(ApplicationInfoProto.Detail.ENABLE_GWP_ASAN, enableGwpAsan);
+ }
proto.end(detailToken);
}
proto.end(token);
@@ -1620,6 +1638,7 @@
mHiddenApiPolicy = orig.mHiddenApiPolicy;
hiddenUntilInstalled = orig.hiddenUntilInstalled;
zygotePreloadName = orig.zygotePreloadName;
+ enableGwpAsan = orig.enableGwpAsan;
}
public String toString() {
@@ -1703,6 +1722,7 @@
dest.writeInt(mHiddenApiPolicy);
dest.writeInt(hiddenUntilInstalled ? 1 : 0);
dest.writeString(zygotePreloadName);
+ sForBoolean.parcel(enableGwpAsan, dest, parcelableFlags);
}
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1783,6 +1803,7 @@
mHiddenApiPolicy = source.readInt();
hiddenUntilInstalled = source.readInt() != 0;
zygotePreloadName = source.readString();
+ enableGwpAsan = sForBoolean.unparcel(source);
}
/**
@@ -2161,6 +2182,7 @@
/** {@hide} */ public void setResourcePath(String resourcePath) { scanPublicSourceDir = resourcePath; }
/** {@hide} */ public void setBaseResourcePath(String baseResourcePath) { publicSourceDir = baseResourcePath; }
/** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
+ /** {@hide} */ public void setGwpAsanEnabled(@Nullable Boolean value) { enableGwpAsan = value; }
/** {@hide} */
@UnsupportedAppUsage
@@ -2172,4 +2194,6 @@
@UnsupportedAppUsage
public String getBaseResourcePath() { return publicSourceDir; }
/** {@hide} */ public String[] getSplitResourcePaths() { return splitPublicSourceDirs; }
+ @Nullable
+ public Boolean isGwpAsanEnabled() { return enableGwpAsan; }
}
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 3261cb1..dc3a029 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -27,6 +27,7 @@
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -95,7 +96,7 @@
mService.startActivityAsUser(
mContext.getIApplicationThread(),
mContext.getPackageName(),
- mContext.getFeatureId(),
+ mContext.getAttributionTag(),
component,
targetUser.getIdentifier(),
true);
@@ -128,14 +129,44 @@
@NonNull Intent intent,
@NonNull UserHandle targetUser,
@Nullable Activity callingActivity) {
+ startActivity(intent, targetUser, callingActivity, /* options= */ null);
+ }
+
+ /**
+ * Starts the specified activity of the caller package in the specified profile.
+ *
+ * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
+ * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
+ * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
+ * target user profiles must be in the same profile group. The target user must be a valid user
+ * returned from {@link #getTargetUserProfiles()}.
+ *
+ * @param intent The intent to launch. A component in the caller package must be specified.
+ * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
+ * {@link #getTargetUserProfiles()} if different to the calling user, otherwise a
+ * {@link SecurityException} will be thrown.
+ * @param callingActivity The activity to start the new activity from for the purposes of
+ * deciding which task the new activity should belong to. If {@code null}, the activity
+ * will always be started in a new task.
+ * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
+ public void startActivity(
+ @NonNull Intent intent,
+ @NonNull UserHandle targetUser,
+ @Nullable Activity callingActivity,
+ @Nullable Bundle options) {
try {
mService.startActivityAsUserByIntent(
mContext.getIApplicationThread(),
mContext.getPackageName(),
- mContext.getFeatureId(),
+ mContext.getAttributionTag(),
intent,
targetUser.getIdentifier(),
- callingActivity != null ? callingActivity.getActivityToken() : null);
+ callingActivity != null ? callingActivity.getActivityToken() : null,
+ options);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -159,7 +190,7 @@
public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
try {
mService.startActivityAsUser(mContext.getIApplicationThread(),
- mContext.getPackageName(), mContext.getFeatureId(), component,
+ mContext.getPackageName(), mContext.getAttributionTag(), component,
targetUser.getIdentifier(), false);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 5a6e008..4cecb30 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -31,7 +31,8 @@
in String callingFeatureId, in ComponentName component, int userId,
boolean launchMainActivity);
void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage,
- in String callingFeatureId, in Intent intent, int userId, in IBinder callingActivity);
+ in String callingFeatureId, in Intent intent, int userId, in IBinder callingActivity,
+ in Bundle options);
List<UserHandle> getTargetUserProfiles(in String callingPackage);
boolean canInteractAcrossProfiles(in String callingPackage);
boolean canRequestInteractAcrossProfiles(in String callingPackage);
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 8a89840..27c9cfc 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -103,4 +103,7 @@
in UserHandle user);
void uncacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
+
+ String getShortcutIconUri(String callingPackage, String packageName, String shortcutId,
+ int userId);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1e10f08..b52034f 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -746,4 +746,6 @@
void clearMimeGroup(String packageName, String group);
List<String> getMimeGroup(String packageName, String group);
+
+ boolean isAutoRevokeWhitelisted(String packageName);
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 86242fd..e73fd03 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -51,6 +51,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -67,8 +68,10 @@
import android.util.Log;
import android.util.Pair;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -460,10 +463,7 @@
/**
* If non-null, return only the specified shortcuts by locus ID. When setting this field,
* a package name must also be set with {@link #setPackage}.
- *
- * @hide
*/
- @SystemApi
@NonNull
public ShortcutQuery setLocusIds(@Nullable List<LocusId> locusIds) {
mLocusIds = locusIds;
@@ -721,7 +721,7 @@
}
try {
mService.startActivityAsUser(mContext.getIApplicationThread(),
- mContext.getPackageName(), mContext.getFeatureId(),
+ mContext.getPackageName(), mContext.getAttributionTag(),
component, sourceBounds, opts, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -739,8 +739,8 @@
@Nullable Rect sourceBounds, @Nullable Bundle opts) {
try {
mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(),
- mContext.getPackageName(), mContext.getFeatureId(), sessionInfo, sourceBounds,
- opts, sessionInfo.getUser());
+ mContext.getPackageName(), mContext.getAttributionTag(), sessionInfo,
+ sourceBounds, opts, sessionInfo.getUser());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -760,7 +760,7 @@
logErrorForInvalidProfileAccess(user);
try {
mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
- mContext.getPackageName(), mContext.getFeatureId(),
+ mContext.getPackageName(), mContext.getAttributionTag(),
component, sourceBounds, opts, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -1201,6 +1201,35 @@
}
/**
+ * @hide internal/unit tests only
+ */
+ @VisibleForTesting
+ public ParcelFileDescriptor getUriShortcutIconFd(@NonNull ShortcutInfo shortcut) {
+ return getUriShortcutIconFd(shortcut.getPackage(), shortcut.getId(), shortcut.getUserId());
+ }
+
+ private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ String uri = null;
+ try {
+ uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
+ userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ if (uri == null) {
+ return null;
+ }
+ try {
+ return mContext.getContentResolver().openFileDescriptor(Uri.parse(uri), "r");
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Icon file not found: " + uri);
+ return null;
+ }
+ }
+
+ /**
* Returns the icon for this shortcut, without any badging for the profile.
*
* <p>The calling launcher application must be allowed to access the shortcut information,
@@ -1220,26 +1249,10 @@
public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
if (shortcut.hasIconFile()) {
final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
- if (pfd == null) {
- return null;
- }
- try {
- final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
- if (bmp != null) {
- BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp);
- if (shortcut.hasAdaptiveBitmap()) {
- return new AdaptiveIconDrawable(null, dr);
- } else {
- return dr;
- }
- }
- return null;
- } finally {
- try {
- pfd.close();
- } catch (IOException ignore) {
- }
- }
+ return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap());
+ } else if (shortcut.hasIconUri()) {
+ final ParcelFileDescriptor pfd = getUriShortcutIconFd(shortcut);
+ return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap());
} else if (shortcut.hasIconResource()) {
return loadDrawableResourceFromPackage(shortcut.getPackage(),
shortcut.getIconResourceId(), shortcut.getUserHandle(), density);
@@ -1263,6 +1276,29 @@
}
}
+ private Drawable loadDrawableFromFileDescriptor(ParcelFileDescriptor pfd, boolean adaptive) {
+ if (pfd == null) {
+ return null;
+ }
+ try {
+ final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+ if (bmp != null) {
+ BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp);
+ if (adaptive) {
+ return new AdaptiveIconDrawable(null, dr);
+ } else {
+ return dr;
+ }
+ }
+ return null;
+ } finally {
+ try {
+ pfd.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
private Drawable loadDrawableResourceFromPackage(String packageName, int resId,
UserHandle user, int density) {
try {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1f53176..50bee85 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -16,6 +16,10 @@
package android.content.pm;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_IGNORED;
+
import android.Manifest;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
@@ -1456,6 +1460,8 @@
/** {@hide} */
public List<String> whitelistedRestrictedPermissions;
/** {@hide} */
+ public int autoRevokePermissionsMode = MODE_DEFAULT;
+ /** {@hide} */
public String installerPackageName;
/** {@hide} */
public boolean isMultiPackage;
@@ -1498,6 +1504,7 @@
volumeUuid = source.readString();
grantedRuntimePermissions = source.readStringArray();
whitelistedRestrictedPermissions = source.createStringArrayList();
+ autoRevokePermissionsMode = source.readInt();
installerPackageName = source.readString();
isMultiPackage = source.readBoolean();
isStaged = source.readBoolean();
@@ -1528,6 +1535,7 @@
ret.volumeUuid = volumeUuid;
ret.grantedRuntimePermissions = grantedRuntimePermissions;
ret.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
+ ret.autoRevokePermissionsMode = autoRevokePermissionsMode;
ret.installerPackageName = installerPackageName;
ret.isMultiPackage = isMultiPackage;
ret.isStaged = isStaged;
@@ -1691,6 +1699,22 @@
}
/**
+ * Sets whether permissions should be auto-revoked if this package is unused for an
+ * extended periodd of time.
+ *
+ * It's disabled by default but generally the installer should enable it for most packages,
+ * excluding only those where doing so might cause breakage that cannot be easily addressed
+ * by simply re-requesting the permission(s).
+ *
+ * If user explicitly enabled or disabled it via settings, this call is ignored.
+ *
+ * @param shouldAutoRevoke whether permissions should be auto-revoked.
+ */
+ public void setAutoRevokePermissionsMode(boolean shouldAutoRevoke) {
+ autoRevokePermissionsMode = shouldAutoRevoke ? MODE_ALLOWED : MODE_IGNORED;
+ }
+
+ /**
* Request that rollbacks be enabled or disabled for the given upgrade with rollback data
* policy set to RESTORE.
*
@@ -1932,6 +1956,7 @@
pw.printPair("volumeUuid", volumeUuid);
pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
pw.printPair("whitelistedRestrictedPermissions", whitelistedRestrictedPermissions);
+ pw.printPair("autoRevokePermissions", autoRevokePermissionsMode);
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("isMultiPackage", isMultiPackage);
pw.printPair("isStaged", isStaged);
@@ -1964,6 +1989,7 @@
dest.writeString(volumeUuid);
dest.writeStringArray(grantedRuntimePermissions);
dest.writeStringList(whitelistedRestrictedPermissions);
+ dest.writeInt(autoRevokePermissionsMode);
dest.writeString(installerPackageName);
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
@@ -2085,6 +2111,8 @@
public String[] grantedRuntimePermissions;
/** {@hide}*/
public List<String> whitelistedRestrictedPermissions;
+ /** {@hide}*/
+ public int autoRevokePermissionsMode = MODE_DEFAULT;
/** {@hide} */
public int installFlags;
/** {@hide} */
@@ -2147,6 +2175,7 @@
referrerUri = source.readParcelable(null);
grantedRuntimePermissions = source.readStringArray();
whitelistedRestrictedPermissions = source.createStringArrayList();
+ autoRevokePermissionsMode = source.readInt();
installFlags = source.readInt();
isMultiPackage = source.readBoolean();
@@ -2323,6 +2352,7 @@
/**
* Get the value set in {@link SessionParams#setOriginatingUri(Uri)}.
+ * Note: This value will only be non-null for the owner of the session.
*/
public @Nullable Uri getOriginatingUri() {
return originatingUri;
@@ -2337,6 +2367,7 @@
/**
* Get the value set in {@link SessionParams#setReferrerUri(Uri)}
+ * Note: This value will only be non-null for the owner of the session.
*/
public @Nullable Uri getReferrerUri() {
return referrerUri;
@@ -2372,6 +2403,24 @@
}
/**
+ * Get the status of whether permission auto-revocation should be allowed, ignored, or
+ * deferred to manifest data.
+ *
+ * @see android.app.AppOpsManager#MODE_ALLOWED
+ * @see android.app.AppOpsManager#MODE_IGNORED
+ * @see android.app.AppOpsManager#MODE_DEFAULT
+ *
+ * @return the status of auto-revoke for this package
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public int getAutoRevokePermissionsMode() {
+ return autoRevokePermissionsMode;
+ }
+
+ /**
* Get the value set in {@link SessionParams#setAllowDowngrade(boolean)}.
*
* @deprecated use {@link #getRequestDowngrade()}.
@@ -2658,6 +2707,7 @@
dest.writeParcelable(referrerUri, flags);
dest.writeStringArray(grantedRuntimePermissions);
dest.writeStringList(whitelistedRestrictedPermissions);
+ dest.writeInt(autoRevokePermissionsMode);
dest.writeInt(installFlags);
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3e9ad01..84eabdb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -557,7 +557,6 @@
* Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
* @hide
*/
- @SystemApi
public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000;
/**
@@ -1978,7 +1977,7 @@
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device's main front and back cameras can stream
* concurrently as described in {@link
- * android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds()}
+ * android.hardware.camera2.CameraManager#getConcurrentCameraIds()}
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";
@@ -3411,12 +3410,13 @@
public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 17;
/**
- * Permission flags: Reserved for use by the permission controller.
- *
+ * Permission flags: Reserved for use by the permission controller. The platform and any
+ * packages besides the permission controller should not assume any definition about these
+ * flags.
* @hide
*/
@SystemApi
- public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = 1 << 28 | 1 << 29
+ public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = 1 << 28 | 1 << 29
| 1 << 30 | 1 << 31;
/**
@@ -4575,6 +4575,53 @@
}
/**
+ * Marks an application exempt from having its permissions be automatically revoked when
+ * the app is unused for an extended period of time.
+ *
+ * Only the installer on record that installed the given package, or a holder of
+ * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+ *
+ * Packages start in whitelisted state, and it is the installer's responsibility to
+ * un-whitelist the packages it installs, unless auto-revoking permissions from that package
+ * would cause breakages beyond having to re-request the permission(s).
+ *
+ * @param packageName The app for which to set exemption.
+ * @param whitelisted Whether the app should be whitelisted.
+ *
+ * @return whether any change took effect.
+ *
+ * @see #isAutoRevokeWhitelisted
+ *
+ * @throws SecurityException if you you have no access to modify this.
+ */
+ @RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
+ conditional = true)
+ public boolean setAutoRevokeWhitelisted(@NonNull String packageName, boolean whitelisted) {
+ return false;
+ }
+
+ /**
+ * Checks whether an application is exempt from having its permissions be automatically revoked
+ * when the app is unused for an extended period of time.
+ *
+ * Only the installer on record that installed the given package, or a holder of
+ * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+ * @param packageName The app for which to set exemption.
+ *
+ * @return Whether the app is whitelisted.
+ *
+ * @see #setAutoRevokeWhitelisted
+ *
+ * @throws SecurityException if you you have no access to this.
+ */
+ @RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
+ conditional = true)
+ public boolean isAutoRevokeWhitelisted(@NonNull String packageName) {
+ return false;
+ }
+
+
+ /**
* Gets whether you should show UI with rationale for requesting a permission.
* You should do this only if you do not have the permission and the context in
* which the permission is requested does not clearly communicate to the user
@@ -7822,6 +7869,15 @@
}
/**
+ * @return whether this package is whitelisted from having its runtime permission be
+ * auto-revoked if unused for an extended period of time.
+ */
+ public boolean isAutoRevokeWhitelisted() {
+ throw new UnsupportedOperationException(
+ "isAutoRevokeWhitelisted not implemented in subclass");
+ }
+
+ /**
* Returns if the provided drawable represents the default activity icon provided by the system.
*
* PackageManager silently returns a default application icon for any package/activity if the
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c6875a4..18f1343 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -55,7 +55,6 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
@@ -190,7 +189,7 @@
public static final String TAG_OVERLAY = "overlay";
public static final String TAG_PACKAGE = "package";
public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
- public static final String TAG_FEATURE = "feature";
+ public static final String TAG_ATTRIBUTION = "attribution";
public static final String TAG_PERMISSION = "permission";
public static final String TAG_PERMISSION_GROUP = "permission-group";
public static final String TAG_PERMISSION_TREE = "permission-tree";
@@ -209,6 +208,8 @@
public static final String TAG_USES_SPLIT = "uses-split";
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
+ public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
+ "android.activity_window_layout_affinity";
/**
* Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
@@ -4560,6 +4561,8 @@
}
}
+ resolveWindowLayout(a);
+
if (!setExported) {
a.info.exported = a.intents.size() > 0;
}
@@ -4726,6 +4729,35 @@
height, heightFraction, gravity, minWidth, minHeight);
}
+ /**
+ * Resolves values in {@link ActivityInfo.WindowLayout}.
+ *
+ * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
+ * Android R and some variants of pre-R.
+ */
+ private void resolveWindowLayout(Activity activity) {
+ // There isn't a metadata for us to fall back. Whatever is in layout is correct.
+ if (activity.metaData == null
+ || !activity.metaData.containsKey(METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+ return;
+ }
+
+ final ActivityInfo aInfo = activity.info;
+ // Layout already specifies a value. We should just use that one.
+ if (aInfo.windowLayout != null && aInfo.windowLayout.windowLayoutAffinity != null) {
+ return;
+ }
+
+ String windowLayoutAffinity = activity.metaData.getString(
+ METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+ if (aInfo.windowLayout == null) {
+ aInfo.windowLayout = new ActivityInfo.WindowLayout(-1 /* width */,
+ -1 /* widthFraction */, -1 /* height */, -1 /* heightFraction */,
+ Gravity.NO_GRAVITY, -1 /* minWidth */, -1 /* minHeight */);
+ }
+ aInfo.windowLayout.windowLayoutAffinity = windowLayoutAffinity;
+ }
+
private Activity parseActivityAlias(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError,
CachedComponentArgs cachedArgs)
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java
index c77a267..a373067 100644
--- a/core/java/android/content/pm/ProcessInfo.java
+++ b/core/java/android/content/pm/ProcessInfo.java
@@ -23,66 +23,157 @@
import android.text.TextUtils;
import android.util.ArraySet;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
/**
* Information about a process an app may run. This corresponds to information collected from the
* AndroidManifest.xml's <permission-group> tags.
* @hide
*/
+@DataClass(genGetters = true, genSetters = false, genParcelable = true, genAidl = false,
+ genBuilder = false)
public class ProcessInfo implements Parcelable {
/**
* The name of the process, fully-qualified based on the app's package name.
*/
+ @NonNull
public String name;
/**
* If non-null, these are permissions that are not allowed in this process.
*/
@Nullable
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringArraySet.class)
public ArraySet<String> deniedPermissions;
- public ProcessInfo(String name, ArraySet<String> deniedPermissions) {
- this.name = name;
- this.deniedPermissions = deniedPermissions;
- }
+ /**
+ * Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified.
+ */
+ @Nullable
+ public Boolean enableGwpAsan;
@Deprecated
public ProcessInfo(@NonNull ProcessInfo orig) {
this.name = orig.name;
this.deniedPermissions = orig.deniedPermissions;
+ this.enableGwpAsan = orig.enableGwpAsan;
}
- public int describeContents() {
- return 0;
+
+
+ // Code below generated by codegen v1.0.15.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ProcessInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new ProcessInfo.
+ *
+ * @param name
+ * The name of the process, fully-qualified based on the app's package name.
+ * @param deniedPermissions
+ * If non-null, these are permissions that are not allowed in this process.
+ * @param enableGwpAsan
+ * Indicates if the process has requested GWP-ASan to be enabled, disabled, or left unspecified.
+ */
+ @DataClass.Generated.Member
+ public ProcessInfo(
+ @NonNull String name,
+ @Nullable ArraySet<String> deniedPermissions,
+ @Nullable Boolean enableGwpAsan) {
+ this.name = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ this.deniedPermissions = deniedPermissions;
+ this.enableGwpAsan = enableGwpAsan;
+
+ // onConstructed(); // You can define this method to get a callback
}
- public void writeToParcel(Parcel dest, int parcelableFlags) {
- dest.writeString(this.name);
- final int numDenied = this.deniedPermissions != null
- ? this.deniedPermissions.size() : 0;
- dest.writeInt(numDenied);
- for (int i = 0; i < numDenied; i++) {
- dest.writeString(this.deniedPermissions.valueAt(i));
+ @DataClass.Generated.Member
+ static Parcelling<ArraySet<String>> sParcellingForDeniedPermissions =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInternedStringArraySet.class);
+ static {
+ if (sParcellingForDeniedPermissions == null) {
+ sParcellingForDeniedPermissions = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInternedStringArraySet());
}
}
- public static final @NonNull Creator<ProcessInfo> CREATOR =
- new Creator<ProcessInfo>() {
- public ProcessInfo createFromParcel(Parcel source) {
- return new ProcessInfo(source);
- }
- public ProcessInfo[] newArray(int size) {
- return new ProcessInfo[size];
- }
- };
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
- private ProcessInfo(Parcel source) {
- this.name = source.readString();
- final int numDenied = source.readInt();
- if (numDenied > 0) {
- this.deniedPermissions = new ArraySet<>(numDenied);
- for (int i = numDenied - 1; i >= 0; i--) {
- this.deniedPermissions.add(TextUtils.safeIntern(source.readString()));
- }
- }
+ byte flg = 0;
+ if (deniedPermissions != null) flg |= 0x2;
+ if (enableGwpAsan != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeString(name);
+ sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+ if (enableGwpAsan != null) dest.writeBoolean(enableGwpAsan);
}
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected ProcessInfo(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String _name = in.readString();
+ ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+ Boolean _enableGwpAsan = (flg & 0x4) == 0 ? null : (Boolean) in.readBoolean();
+
+ this.name = _name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ this.deniedPermissions = _deniedPermissions;
+ this.enableGwpAsan = _enableGwpAsan;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ProcessInfo> CREATOR
+ = new Parcelable.Creator<ProcessInfo>() {
+ @Override
+ public ProcessInfo[] newArray(int size) {
+ return new ProcessInfo[size];
+ }
+
+ @Override
+ public ProcessInfo createFromParcel(@NonNull Parcel in) {
+ return new ProcessInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1582840056156L,
+ codegenVersion = "1.0.15",
+ sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java",
+ inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.annotation.Nullable java.lang.Boolean enableGwpAsan\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 49e8c05..8d8776f 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -123,6 +123,9 @@
public static final int FLAG_CACHED = 1 << 14;
/** @hide */
+ public static final int FLAG_HAS_ICON_URI = 1 << 15;
+
+ /** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_DYNAMIC,
FLAG_PINNED,
@@ -139,6 +142,7 @@
FLAG_SHADOW,
FLAG_LONG_LIVED,
FLAG_CACHED,
+ FLAG_HAS_ICON_URI,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShortcutFlags {}
@@ -401,6 +405,9 @@
private String mIconResName;
// Internal use only.
+ private String mIconUri;
+
+ // Internal use only.
@Nullable
private String mBitmapPath;
@@ -554,6 +561,7 @@
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
mBitmapPath = source.mBitmapPath;
+ mIconUri = source.mIconUri;
}
mTitle = source.mTitle;
@@ -856,6 +864,7 @@
mIconResId = 0;
mIconResName = null;
mBitmapPath = null;
+ mIconUri = null;
}
if (source.mTitle != null) {
mTitle = source.mTitle;
@@ -916,6 +925,8 @@
case Icon.TYPE_RESOURCE:
case Icon.TYPE_BITMAP:
case Icon.TYPE_ADAPTIVE_BITMAP:
+ case Icon.TYPE_URI:
+ case Icon.TYPE_URI_ADAPTIVE_BITMAP:
break; // OK
default:
throw getInvalidIconException();
@@ -1558,11 +1569,6 @@
* "Rank" of a shortcut, which is a non-negative, sequential value that's unique for each
* {@link #getActivity} for each of the two types of shortcuts (static and dynamic).
*
- * <p>Because static shortcuts and dynamic shortcuts have overlapping ranks,
- * when a launcher app shows shortcuts for an activity, it should first show
- * the static shortcuts, followed by the dynamic shortcuts. Within each of those categories,
- * shortcuts should be sorted by rank in ascending order.
- *
* <p><em>Floating shortcuts</em>, or shortcuts that are neither static nor dynamic, will all
* have rank 0, because they aren't sorted.
*
@@ -1797,6 +1803,15 @@
return hasFlags(FLAG_HAS_ICON_RES);
}
+ /**
+ * Return whether a shortcut's icon is provided via a URI.
+ *
+ * @hide internal/unit tests only
+ */
+ public boolean hasIconUri() {
+ return hasFlags(FLAG_HAS_ICON_URI);
+ }
+
/** @hide */
public boolean hasStringResources() {
return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0);
@@ -1916,6 +1931,19 @@
return mIconResId;
}
+ /** @hide */
+ public void setIconUri(String iconUri) {
+ mIconUri = iconUri;
+ }
+
+ /**
+ * Get the Uri for the icon, valid only when {@link #hasIconUri()} } is true.
+ * @hide internal / tests only.
+ */
+ public String getIconUri() {
+ return mIconUri;
+ }
+
/**
* Bitmap path. Note this will be null even if {@link #hasIconFile()} is set when the save
* is pending. Use {@link #isIconPendingSave()} to check it.
@@ -2067,6 +2095,7 @@
mPersons = source.readParcelableArray(cl, Person.class);
mLocusId = source.readParcelable(cl);
+ mIconUri = source.readString();
}
@Override
@@ -2117,6 +2146,7 @@
dest.writeParcelableArray(mPersons, flags);
dest.writeParcelable(mLocusId, flags);
+ dest.writeString(mIconUri);
}
public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR =
@@ -2208,6 +2238,12 @@
if (hasIconResource()) {
sb.append("Ic-r");
}
+ if (hasIconUri()) {
+ sb.append("Ic-u");
+ }
+ if (hasAdaptiveBitmap()) {
+ sb.append("Ic-a");
+ }
if (hasKeyFieldsOnly()) {
sb.append("Key");
}
@@ -2330,6 +2366,9 @@
sb.append(", bitmapPath=");
sb.append(mBitmapPath);
+
+ sb.append(", iconUri=");
+ sb.append(mIconUri);
}
if (mLocusId != null) {
@@ -2348,8 +2387,8 @@
CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
- int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason,
- Person[] persons, LocusId locusId) {
+ int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
+ int disabledReason, Person[] persons, LocusId locusId) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2374,6 +2413,7 @@
mIconResId = iconResId;
mIconResName = iconResName;
mBitmapPath = bitmapPath;
+ mIconUri = iconUri;
mDisabledReason = disabledReason;
mPersons = persons;
mLocusId = locusId;
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index a50ce92..435c70a 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -103,4 +103,10 @@
*/
public abstract List<ShortcutManager.ShareShortcutInfo> getShareTargets(
@NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId);
+
+ /**
+ * Returns the icon Uri of the shortcut, and grants Uri read permission to the caller.
+ */
+ public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId);
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index aa93d80..1304ba8 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -24,7 +24,7 @@
import android.content.pm.FeatureInfo;
import android.content.pm.PackageParser;
import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedPermission;
@@ -77,7 +77,7 @@
ParsingPackage addProvider(ParsedProvider parsedProvider);
- ParsingPackage addFeature(ParsedFeature permission);
+ ParsingPackage addAttribution(ParsedAttribution attribution);
ParsingPackage addReceiver(ParsedActivity parsedReceiver);
@@ -191,7 +191,11 @@
ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
-
+
+ ParsingPackage setDontAutoRevokePermissions(boolean dontAutoRevokePermissions);
+
+ ParsingPackage setAllowDontAutoRevokePermissions(boolean allowDontAutoRevokePermissions);
+
ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
@@ -236,6 +240,8 @@
ParsingPackage setEnabled(boolean enabled);
+ ParsingPackage setGwpAsanEnabled(Boolean enableGwpAsan);
+
ParsingPackage setCrossProfile(boolean crossProfile);
ParsingPackage setFullBackupContent(int fullBackupContent);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index a9b72d0..3390f16 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -30,9 +30,10 @@
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
+import android.content.pm.ProcessInfo;
import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedComponent;
-import android.content.pm.parsing.component.ParsedFeature;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
@@ -243,7 +244,7 @@
protected List<ParsedProvider> providers = emptyList();
@NonNull
- private List<ParsedFeature> features = emptyList();
+ private List<ParsedAttribution> attributions = emptyList();
@NonNull
protected List<ParsedPermission> permissions = emptyList();
@@ -404,8 +405,14 @@
private boolean hasFragileUserData;
private boolean cantSaveState;
private boolean allowNativeHeapPointerTagging;
+ private boolean dontAutoRevokePermissions;
+ private boolean allowDontAutoRevokePermissions;
private boolean preserveLegacyExternalStorage;
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ protected Boolean enableGwpAsan = null;
+
// TODO(chiuwinson): Non-null
@Nullable
private ArraySet<String> mimeGroups;
@@ -620,8 +627,8 @@
}
@Override
- public ParsingPackageImpl addFeature(ParsedFeature feature) {
- this.features = CollectionUtils.add(this.features, feature);
+ public ParsingPackageImpl addAttribution(ParsedAttribution attribution) {
+ this.attributions = CollectionUtils.add(this.attributions, attribution);
return this;
}
@@ -904,7 +911,7 @@
appInfo.volumeUuid = volumeUuid;
appInfo.zygotePreloadName = zygotePreloadName;
appInfo.crossProfile = isCrossProfile();
-
+ appInfo.setGwpAsanEnabled(enableGwpAsan);
appInfo.setBaseCodePath(baseCodePath);
appInfo.setBaseResourcePath(baseCodePath);
appInfo.setCodePath(codePath);
@@ -990,7 +997,7 @@
dest.writeTypedList(this.receivers);
dest.writeTypedList(this.services);
dest.writeTypedList(this.providers);
- dest.writeTypedList(this.features);
+ dest.writeTypedList(this.attributions);
dest.writeTypedList(this.permissions);
dest.writeTypedList(this.permissionGroups);
dest.writeTypedList(this.instrumentations);
@@ -1084,8 +1091,11 @@
dest.writeBoolean(this.hasFragileUserData);
dest.writeBoolean(this.cantSaveState);
dest.writeBoolean(this.allowNativeHeapPointerTagging);
+ dest.writeBoolean(this.dontAutoRevokePermissions);
+ dest.writeBoolean(this.allowDontAutoRevokePermissions);
dest.writeBoolean(this.preserveLegacyExternalStorage);
dest.writeArraySet(this.mimeGroups);
+ sForBoolean.parcel(this.enableGwpAsan, dest, flags);
}
public ParsingPackageImpl(Parcel in) {
@@ -1149,7 +1159,7 @@
this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR);
this.services = in.createTypedArrayList(ParsedService.CREATOR);
this.providers = in.createTypedArrayList(ParsedProvider.CREATOR);
- this.features = in.createTypedArrayList(ParsedFeature.CREATOR);
+ this.attributions = in.createTypedArrayList(ParsedAttribution.CREATOR);
this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR);
this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR);
this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR);
@@ -1241,8 +1251,11 @@
this.hasFragileUserData = in.readBoolean();
this.cantSaveState = in.readBoolean();
this.allowNativeHeapPointerTagging = in.readBoolean();
+ this.dontAutoRevokePermissions = in.readBoolean();
+ this.allowDontAutoRevokePermissions = in.readBoolean();
this.preserveLegacyExternalStorage = in.readBoolean();
this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
+ this.enableGwpAsan = sForBoolean.unparcel(in);
}
public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
@@ -1509,8 +1522,8 @@
@NonNull
@Override
- public List<ParsedFeature> getFeatures() {
- return features;
+ public List<ParsedAttribution> getAttributions() {
+ return attributions;
}
@NonNull
@@ -1965,6 +1978,12 @@
}
@Override
+ @Nullable
+ public Boolean isGwpAsanEnabled() {
+ return enableGwpAsan;
+ }
+
+ @Override
public boolean isPartiallyDirectBootAware() {
return partiallyDirectBootAware;
}
@@ -2010,6 +2029,16 @@
}
@Override
+ public boolean isDontAutoRevokePermmissions() {
+ return dontAutoRevokePermissions;
+ }
+
+ @Override
+ public boolean isAllowDontAutoRevokePermmissions() {
+ return allowDontAutoRevokePermissions;
+ }
+
+ @Override
public boolean hasPreserveLegacyExternalStorage() {
return preserveLegacyExternalStorage;
}
@@ -2420,6 +2449,12 @@
}
@Override
+ public ParsingPackageImpl setGwpAsanEnabled(@Nullable Boolean value) {
+ enableGwpAsan = value;
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
partiallyDirectBootAware = value;
return this;
@@ -2474,6 +2509,18 @@
}
@Override
+ public ParsingPackageImpl setDontAutoRevokePermissions(boolean value) {
+ dontAutoRevokePermissions = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAllowDontAutoRevokePermissions(boolean value) {
+ allowDontAutoRevokePermissions = value;
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl setPreserveLegacyExternalStorage(boolean value) {
preserveLegacyExternalStorage = value;
return this;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 048b924..9c13c85 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -28,7 +28,7 @@
import android.content.pm.PackageParser;
import android.content.pm.ServiceInfo;
import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedPermission;
@@ -80,7 +80,7 @@
List<ConfigurationInfo> getConfigPreferences();
@NonNull
- List<ParsedFeature> getFeatures();
+ List<ParsedAttribution> getAttributions();
/**
* @see PackageInfo#featureGroups
@@ -771,6 +771,12 @@
/** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING */
boolean isAllowNativeHeapPointerTagging();
+ /** @see ApplicationInfo#PRIVATE_FLAG2_DONT_AUTO_REVOKE_PERMISSIONS */
+ boolean isDontAutoRevokePermmissions();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG2_ALLOW_DONT_AUTO_REVOKE_PERMISSIONS */
+ boolean isAllowDontAutoRevokePermmissions();
+
boolean hasPreserveLegacyExternalStorage();
/**
@@ -840,6 +846,13 @@
@Nullable
Set<String> getMimeGroups();
+ /**
+ * @see ApplicationInfo#enableGwpAsan
+ * @see R.styleable#AndroidManifest_enableGwpAsan
+ */
+ @Nullable
+ public Boolean isGwpAsanEnabled();
+
// TODO(b/135203078): Hide and enforce going through PackageInfoUtils
ApplicationInfo toAppInfoWithoutState();
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index b4f2159..e41ed85 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -40,6 +40,7 @@
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
@@ -48,8 +49,8 @@
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedActivityUtils;
-import android.content.pm.parsing.component.ParsedFeature;
-import android.content.pm.parsing.component.ParsedFeatureUtils;
+import android.content.pm.parsing.component.ParsedAttribution;
+import android.content.pm.parsing.component.ParsedAttributionUtils;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedInstrumentationUtils;
import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -672,7 +673,7 @@
);
}
- if (!ParsedFeature.isCombinationValid(pkg.getFeatures())) {
+ if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) {
return input.error(
INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Combination <feature> tags are not valid"
@@ -707,8 +708,9 @@
return parseOverlay(input, pkg, res, parser);
case PackageParser.TAG_KEY_SETS:
return parseKeySets(input, pkg, res, parser);
- case PackageParser.TAG_FEATURE:
- return parseFeature(input, pkg, res, parser);
+ case "feature": // TODO moltmann: Remove
+ case PackageParser.TAG_ATTRIBUTION:
+ return parseAttribution(input, pkg, res, parser);
case PackageParser.TAG_PERMISSION_GROUP:
return parsePermissionGroup(input, pkg, res, parser);
case PackageParser.TAG_PERMISSION:
@@ -917,13 +919,15 @@
return input.success(pkg);
}
- private static ParseResult<ParsingPackage> parseFeature(ParseInput input, ParsingPackage pkg,
- Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
- ParseResult<ParsedFeature> result = ParsedFeatureUtils.parseFeature(res, parser, input);
+ private static ParseResult<ParsingPackage> parseAttribution(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws IOException, XmlPullParserException {
+ ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res,
+ parser, input);
if (result.isError()) {
return input.error(result);
}
- return input.success(pkg.addFeature(result.getResult()));
+ return input.success(pkg.addAttribution(result.getResult()));
}
private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
@@ -1660,6 +1664,11 @@
&& !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
return input.error("Invalid class loader name: " + classLoaderName);
}
+
+ if (sa.hasValue(R.styleable.AndroidManifestApplication_enableGwpAsan)) {
+ pkg.setGwpAsanEnabled(
+ sa.getBoolean(R.styleable.AndroidManifestApplication_enableGwpAsan, false));
+ }
} finally {
sa.recycle();
}
@@ -1811,6 +1820,8 @@
.setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
.setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
.setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
+ .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestDontAutoRevokePermissions, sa))
+ .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowDontAutoRevokePermissions, sa))
// targetSdkVersion gated
.setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
.setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index d32171d..4c93d09 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -285,14 +285,8 @@
dest.writeBundle(this.metaData);
if (windowLayout != null) {
- dest.writeBoolean(true);
- dest.writeInt(windowLayout.width);
- dest.writeFloat(windowLayout.widthFraction);
- dest.writeInt(windowLayout.height);
- dest.writeFloat(windowLayout.heightFraction);
- dest.writeInt(windowLayout.gravity);
- dest.writeInt(windowLayout.minWidth);
- dest.writeInt(windowLayout.minHeight);
+ dest.writeInt(1);
+ windowLayout.writeToParcel(dest);
} else {
dest.writeBoolean(false);
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 1dcf262..6e5c51a 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -17,16 +17,17 @@
package android.content.pm.parsing.component;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
import static android.content.pm.parsing.component.ComponentParseUtils.flag;
import android.annotation.NonNull;
import android.app.ActivityTaskManager;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageParser;
-
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -42,9 +43,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -379,6 +377,12 @@
}
}
+ ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input);
+ if (layoutResult.isError()) {
+ return input.error(layoutResult);
+ }
+ activity.windowLayout = layoutResult.getResult();
+
if (!setExported) {
activity.exported = activity.getIntents().size() > 0;
}
@@ -481,4 +485,35 @@
sw.recycle();
}
}
+
+ /**
+ * Resolves values in {@link ActivityInfo.WindowLayout}.
+ *
+ * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
+ * Android R and some variants of pre-R.
+ */
+ private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout(
+ ParsedActivity activity, ParseInput input) {
+ // There isn't a metadata for us to fall back. Whatever is in layout is correct.
+ if (activity.metaData == null || !activity.metaData.containsKey(
+ PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+ return input.success(activity.windowLayout);
+ }
+
+ // Layout already specifies a value. We should just use that one.
+ if (activity.windowLayout != null && activity.windowLayout.windowLayoutAffinity != null) {
+ return input.success(activity.windowLayout);
+ }
+
+ String windowLayoutAffinity = activity.metaData.getString(
+ PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+ ActivityInfo.WindowLayout layout = activity.windowLayout;
+ if (layout == null) {
+ layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
+ -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
+ -1 /* minWidth */, -1 /* minHeight */);
+ }
+ layout.windowLayoutAffinity = windowLayoutAffinity;
+ return input.success(layout);
+ }
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
new file mode 100644
index 0000000..02b3c7d
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
@@ -0,0 +1,229 @@
+/*
+ * 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 android.content.pm.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestAttribution <attribution>} tag parsed from the
+ * manifest.
+ *
+ * @hide
+ */
+@DataClass(genAidl = false)
+public class ParsedAttribution implements Parcelable {
+ /** Maximum length of attribution tag */
+ public static final int MAX_ATTRIBUTION_TAG_LEN = 50;
+
+ /** Maximum amount of attributions per package */
+ private static final int MAX_NUM_ATTRIBUTIONS = 1000;
+
+ /** Tag of the attribution */
+ public final @NonNull String tag;
+
+ /** User visible label fo the attribution */
+ public final @StringRes int label;
+
+ /** Ids of previously declared attributions this attribution inherits from */
+ public final @NonNull List<String> inheritFrom;
+
+ /**
+ * @return Is this set of attributions a valid combination for a single package?
+ */
+ public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) {
+ if (attributions == null) {
+ return true;
+ }
+
+ ArraySet<String> attributionTags = new ArraySet<>(attributions.size());
+ ArraySet<String> inheritFromAttributionTags = new ArraySet<>();
+
+ int numAttributions = attributions.size();
+ if (numAttributions > MAX_NUM_ATTRIBUTIONS) {
+ return false;
+ }
+
+ for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+ boolean wasAdded = attributionTags.add(attributions.get(attributionNum).tag);
+ if (!wasAdded) {
+ // feature id is not unique
+ return false;
+ }
+ }
+
+ for (int attributionNum = 0; attributionNum < numAttributions; attributionNum++) {
+ ParsedAttribution feature = attributions.get(attributionNum);
+
+ int numInheritFrom = feature.inheritFrom.size();
+ for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
+ String inheritFrom = feature.inheritFrom.get(inheritFromNum);
+
+ if (attributionTags.contains(inheritFrom)) {
+ // Cannot inherit from a attribution that is still defined
+ return false;
+ }
+
+ boolean wasAdded = inheritFromAttributionTags.add(inheritFrom);
+ if (!wasAdded) {
+ // inheritFrom is not unique
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @android.annotation.IntDef(prefix = "MAX_", value = {
+ MAX_ATTRIBUTION_TAG_LEN,
+ MAX_NUM_ATTRIBUTIONS
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface Max {}
+
+ @DataClass.Generated.Member
+ public static String maxToString(@Max int value) {
+ switch (value) {
+ case MAX_ATTRIBUTION_TAG_LEN:
+ return "MAX_ATTRIBUTION_TAG_LEN";
+ case MAX_NUM_ATTRIBUTIONS:
+ return "MAX_NUM_ATTRIBUTIONS";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ /**
+ * Creates a new ParsedAttribution.
+ *
+ * @param tag
+ * Tag of the attribution
+ * @param label
+ * User visible label fo the attribution
+ * @param inheritFrom
+ * Ids of previously declared attributions this attribution inherits from
+ */
+ @DataClass.Generated.Member
+ public ParsedAttribution(
+ @NonNull String tag,
+ @StringRes int label,
+ @NonNull List<String> inheritFrom) {
+ this.tag = tag;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, tag);
+ this.label = label;
+ com.android.internal.util.AnnotationValidations.validate(
+ StringRes.class, null, label);
+ this.inheritFrom = inheritFrom;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, inheritFrom);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeString(tag);
+ dest.writeInt(label);
+ dest.writeStringList(inheritFrom);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected ParsedAttribution(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ String _tag = in.readString();
+ int _label = in.readInt();
+ List<String> _inheritFrom = new ArrayList<>();
+ in.readStringList(_inheritFrom);
+
+ this.tag = _tag;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, tag);
+ this.label = _label;
+ com.android.internal.util.AnnotationValidations.validate(
+ StringRes.class, null, label);
+ this.inheritFrom = _inheritFrom;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, inheritFrom);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ParsedAttribution> CREATOR
+ = new Parcelable.Creator<ParsedAttribution>() {
+ @Override
+ public ParsedAttribution[] newArray(int size) {
+ return new ParsedAttribution[size];
+ }
+
+ @Override
+ public ParsedAttribution createFromParcel(@NonNull Parcel in) {
+ return new ParsedAttribution(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1583436566499L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
+ inputSignatures = "public static final int MAX_ATTRIBUTION_TAG_LEN\nprivate static final int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
similarity index 63%
rename from core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java
rename to core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
index fb52801..c4b1a0e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java
@@ -34,34 +34,40 @@
import java.util.List;
/** @hide */
-public class ParsedFeatureUtils {
+public class ParsedAttributionUtils {
@NonNull
- public static ParseResult<ParsedFeature> parseFeature(Resources res, XmlResourceParser parser,
- ParseInput input) throws IOException, XmlPullParserException {
- String featureId;
+ public static ParseResult<ParsedAttribution> parseAttribution(Resources res,
+ XmlResourceParser parser, ParseInput input)
+ throws IOException, XmlPullParserException {
+ String attributionTag;
int label;
List<String> inheritFrom = null;
- TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature);
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAttribution);
if (sa == null) {
- return input.error("<feature> could not be parsed");
+ return input.error("<attribution> could not be parsed");
}
try {
- featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId,
- 0);
- if (featureId == null) {
- return input.error("<featureId> does not specify android:featureId");
+ attributionTag = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestAttribution_tag, 0);
+ if (attributionTag == null) {
+ // TODO moltmann: Remove handling of featureId
+ attributionTag = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestAttribution_featureId, 0);
+ if (attributionTag == null) {
+ return input.error("<attribution> does not specify android:tag");
+ }
}
- if (featureId.length() > ParsedFeature.MAX_FEATURE_ID_LEN) {
- return input.error("<featureId> is too long. Max length is "
- + ParsedFeature.MAX_FEATURE_ID_LEN);
+ if (attributionTag.length() > ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN) {
+ return input.error("android:tag is too long. Max length is "
+ + ParsedAttribution.MAX_ATTRIBUTION_TAG_LEN);
}
- label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0);
+ label = sa.getResourceId(R.styleable.AndroidManifestAttribution_label, 0);
if (label == Resources.ID_NULL) {
- return input.error("<featureId> does not specify android:label");
+ return input.error("<attribution> does not specify android:label");
}
} finally {
sa.recycle();
@@ -77,14 +83,15 @@
String tagName = parser.getName();
if (tagName.equals("inherit-from")) {
- sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeatureInheritFrom);
+ sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestAttributionInheritFrom);
if (sa == null) {
return input.error("<inherit-from> could not be parsed");
}
try {
String inheritFromId = sa.getNonConfigurationString(
- R.styleable.AndroidManifestFeatureInheritFrom_featureId,0);
+ R.styleable.AndroidManifestAttributionInheritFrom_tag, 0);
if (inheritFrom == null) {
inheritFrom = new ArrayList<>();
@@ -94,7 +101,7 @@
sa.recycle();
}
} else {
- return input.error("Bad element under <feature>: " + tagName);
+ return input.error("Bad element under <attribution>: " + tagName);
}
}
@@ -104,6 +111,6 @@
((ArrayList) inheritFrom).trimToSize();
}
- return input.success(new ParsedFeature(featureId, label, inheritFrom));
+ return input.success(new ParsedAttribution(attributionTag, label, inheritFrom));
}
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedFeature.java b/core/java/android/content/pm/parsing/component/ParsedFeature.java
deleted file mode 100644
index b8a9098..0000000
--- a/core/java/android/content/pm/parsing/component/ParsedFeature.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.parsing.component;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringRes;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.ArraySet;
-
-import com.android.internal.util.DataClass;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A {@link android.R.styleable#AndroidManifestFeature <feature>} tag parsed from the
- * manifest.
- *
- * @hide
- */
-@DataClass(genAidl = false)
-public class ParsedFeature implements Parcelable {
- /** Maximum length of featureId */
- public static final int MAX_FEATURE_ID_LEN = 50;
-
- /** Maximum amount of features per package */
- private static final int MAX_NUM_FEATURES = 1000;
-
- /** Id of the feature */
- public final @NonNull String id;
-
- /** User visible label fo the feature */
- public final @StringRes int label;
-
- /** Ids of previously declared features this feature inherits from */
- public final @NonNull List<String> inheritFrom;
-
- /**
- * @return Is this set of features a valid combination for a single package?
- */
- public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) {
- if (features == null) {
- return true;
- }
-
- ArraySet<String> featureIds = new ArraySet<>(features.size());
- ArraySet<String> inheritFromFeatureIds = new ArraySet<>();
-
- int numFeatures = features.size();
- if (numFeatures > MAX_NUM_FEATURES) {
- return false;
- }
-
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- boolean wasAdded = featureIds.add(features.get(featureNum).id);
- if (!wasAdded) {
- // feature id is not unique
- return false;
- }
- }
-
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- ParsedFeature feature = features.get(featureNum);
-
- int numInheritFrom = feature.inheritFrom.size();
- for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
- String inheritFrom = feature.inheritFrom.get(inheritFromNum);
-
- if (featureIds.contains(inheritFrom)) {
- // Cannot inherit from a feature that is still defined
- return false;
- }
-
- boolean wasAdded = inheritFromFeatureIds.add(inheritFrom);
- if (!wasAdded) {
- // inheritFrom is not unique
- return false;
- }
- }
- }
-
- return true;
- }
-
-
-
- // Code below generated by codegen v1.0.14.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @android.annotation.IntDef(prefix = "MAX_", value = {
- MAX_FEATURE_ID_LEN,
- MAX_NUM_FEATURES
- })
- @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
- @DataClass.Generated.Member
- public @interface Max {}
-
- @DataClass.Generated.Member
- public static String maxToString(@Max int value) {
- switch (value) {
- case MAX_FEATURE_ID_LEN:
- return "MAX_FEATURE_ID_LEN";
- case MAX_NUM_FEATURES:
- return "MAX_NUM_FEATURES";
- default: return Integer.toHexString(value);
- }
- }
-
- /**
- * Creates a new ParsedFeature.
- *
- * @param id
- * Id of the feature
- * @param label
- * User visible label fo the feature
- * @param inheritFrom
- * Ids of previously declared features this feature inherits from
- */
- @DataClass.Generated.Member
- public ParsedFeature(
- @NonNull String id,
- @StringRes int label,
- @NonNull List<String> inheritFrom) {
- this.id = id;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, id);
- this.label = label;
- com.android.internal.util.AnnotationValidations.validate(
- StringRes.class, null, label);
- this.inheritFrom = inheritFrom;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, inheritFrom);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- dest.writeString(id);
- dest.writeInt(label);
- dest.writeStringList(inheritFrom);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- protected ParsedFeature(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- String _id = in.readString();
- int _label = in.readInt();
- List<String> _inheritFrom = new ArrayList<>();
- in.readStringList(_inheritFrom);
-
- this.id = _id;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, id);
- this.label = _label;
- com.android.internal.util.AnnotationValidations.validate(
- StringRes.class, null, label);
- this.inheritFrom = _inheritFrom;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, inheritFrom);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR
- = new Parcelable.Creator<ParsedFeature>() {
- @Override
- public ParsedFeature[] newArray(int size) {
- return new ParsedFeature[size];
- }
-
- @Override
- public ParsedFeature createFromParcel(@NonNull Parcel in) {
- return new ParsedFeature(in);
- }
- };
-
- @DataClass.Generated(
- time = 1581379861853L,
- codegenVersion = "1.0.14",
- sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java",
- inputSignatures = "public static final int MAX_FEATURE_ID_LEN\nprivate static final int MAX_NUM_FEATURES\npublic final @android.annotation.NonNull java.lang.String id\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedFeature>)\nclass ParsedFeature extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index da7bf98..3b6020a 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -41,6 +41,9 @@
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
protected Set<String> deniedPermissions = emptySet();
+ @Nullable
+ protected Boolean enableGwpAsan = null;
+
public ParsedProcess() {
}
@@ -71,13 +74,15 @@
@DataClass.Generated.Member
public ParsedProcess(
@NonNull String name,
- @NonNull Set<String> deniedPermissions) {
+ @NonNull Set<String> deniedPermissions,
+ @Nullable Boolean enableGwpAsan) {
this.name = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
this.deniedPermissions = deniedPermissions;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
+ this.enableGwpAsan = enableGwpAsan;
// onConstructed(); // You can define this method to get a callback
}
@@ -93,6 +98,11 @@
}
@DataClass.Generated.Member
+ public @Nullable Boolean getEnableGwpAsan() {
+ return enableGwpAsan;
+ }
+
+ @DataClass.Generated.Member
static Parcelling<Set<String>> sParcellingForDeniedPermissions =
Parcelling.Cache.get(
Parcelling.BuiltIn.ForInternedStringSet.class);
@@ -109,8 +119,12 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ byte flg = 0;
+ if (enableGwpAsan != null) flg |= 0x4;
+ dest.writeByte(flg);
dest.writeString(name);
sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+ if (enableGwpAsan != null) dest.writeBoolean(enableGwpAsan);
}
@Override
@@ -124,8 +138,10 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
String _name = in.readString();
Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+ Boolean _enableGwpAsan = (flg & 0x4) == 0 ? null : (Boolean) in.readBoolean();
this.name = _name;
com.android.internal.util.AnnotationValidations.validate(
@@ -133,6 +149,7 @@
this.deniedPermissions = _deniedPermissions;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
+ this.enableGwpAsan = _enableGwpAsan;
// onConstructed(); // You can define this method to get a callback
}
@@ -152,10 +169,10 @@
};
@DataClass.Generated(
- time = 1581452315946L,
+ time = 1582589960479L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
- inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+ inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected @android.annotation.Nullable java.lang.Boolean enableGwpAsan\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index 4825066..3820790 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -103,6 +103,11 @@
if (proc.name == null || proc.name.length() <= 0) {
return input.error("<process> does not specify android:process");
}
+
+ if (sa.hasValue(R.styleable.AndroidManifestProcess_enableGwpAsan)) {
+ proc.enableGwpAsan =
+ sa.getBoolean(R.styleable.AndroidManifestProcess_enableGwpAsan, false);
+ }
} finally {
sa.recycle();
}
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 5fbf0da..9906331 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -657,7 +657,9 @@
public int accuracy;
/**
- * The time in nanosecond at which the event happened
+ * The time in nanoseconds at which the event happened. For a given sensor,
+ * each new sensor event should be monotonically increasing using the same
+ * time base as {@link android.os.SystemClock#elapsedRealtimeNanos()}.
*/
public long timestamp;
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
index 61310f3..8bcaf82 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -35,7 +35,7 @@
// Notifies that a biometric has been acquired.
void onAcquired(int acquiredInfo, String message);
// Notifies that the SystemUI dialog has been dismissed.
- void onDialogDismissed(int reason);
+ void onDialogDismissed(int reason, in byte[] credentialAttestation);
// Notifies that the user has pressed the "try again" button on SystemUI
void onTryAgainPressed();
// Notifies that the user has pressed the "use password" button on SystemUI
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index b3a1ee2..7e72b73d 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2884,12 +2884,12 @@
* generated according to the documented
* {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each device
* which has its Id present in the set returned by
- * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.
+ * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds}.
* Clients can use the array as a quick reference to find an appropriate camera stream
* combination.
* The mandatory stream combination array will be {@code null} in case the device is not a part
* of at least one set of combinations returned by
- * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.</p>
+ * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds}.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
*/
@PublicKey
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index cc0c1a30..30ee326 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -681,7 +681,7 @@
* </p>
*
*<p>Devices capable of streaming concurrently with other devices as described by
- * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds} have the
+ * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the
* following guaranteed streams (when streaming concurrently with other devices)</p>
*
* <table>
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index a091f84..e81c649 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -160,8 +160,8 @@
* @throws CameraAccessException if the camera device has been disconnected.
*/
@NonNull
- public Set<Set<String>> getConcurrentStreamingCameraIds() throws CameraAccessException {
- return CameraManagerGlobal.get().getConcurrentStreamingCameraIds();
+ public Set<Set<String>> getConcurrentCameraIds() throws CameraAccessException {
+ return CameraManagerGlobal.get().getConcurrentCameraIds();
}
/**
@@ -189,11 +189,11 @@
*
* @return {@code true} if the given combination of session configurations and corresponding
* camera ids are concurrently supported by the camera sub-system,
- * {@code false} otherwise.
+ * {@code false} otherwise OR if the set of camera devices provided is not a subset of
+ * those returned by {@link #getConcurrentCameraIds}.
*
- * @throws IllegalArgumentException if the set of camera devices provided is not a subset of
- * those returned by getConcurrentStreamingCameraIds()
* @throws CameraAccessException if one of the camera devices queried is no longer connected.
+ *
*/
@RequiresPermission(android.Manifest.permission.CAMERA)
public boolean isConcurrentSessionConfigurationSupported(
@@ -486,7 +486,7 @@
"Camera service is currently unavailable");
}
cameraUser = cameraService.connectDevice(callbacks, cameraId,
- mContext.getOpPackageName(), mContext.getFeatureId(), uid);
+ mContext.getOpPackageName(), mContext.getAttributionTag(), uid);
} else {
// Use legacy camera implementation for HAL1 devices
int id;
@@ -1183,7 +1183,7 @@
try {
ConcurrentCameraIdCombination[] cameraIdCombinations =
- cameraService.getConcurrentStreamingCameraIds();
+ cameraService.getConcurrentCameraIds();
for (ConcurrentCameraIdCombination comb : cameraIdCombinations) {
mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination());
}
@@ -1372,7 +1372,7 @@
return cameraIds;
}
- public @NonNull Set<Set<String>> getConcurrentStreamingCameraIds() {
+ public @NonNull Set<Set<String>> getConcurrentCameraIds() {
Set<Set<String>> concurrentStreamingCameraIds = null;
synchronized (mLock) {
// Try to make sure we have an up-to-date list of concurrent camera devices.
@@ -1398,7 +1398,7 @@
synchronized (mLock) {
// Go through all the elements and check if the camera ids are valid at least /
- // belong to one of the combinations returned by getConcurrentStreamingCameraIds()
+ // belong to one of the combinations returned by getConcurrentCameraIds()
boolean subsetFound = false;
for (Set<String> combination : mConcurrentCameraIdCombinations) {
if (combination.containsAll(cameraIdsAndSessionConfigurations.keySet())) {
@@ -1406,9 +1406,9 @@
}
}
if (!subsetFound) {
- throw new IllegalArgumentException(
- "The set of camera ids provided is not a subset of"
- + "getConcurrentStreamingCameraIds");
+ Log.v(TAG, "isConcurrentSessionConfigurationSupported called with a subset of"
+ + "camera ids not returned by getConcurrentCameraIds");
+ return false;
}
CameraIdAndSessionConfiguration [] cameraIdsAndConfigs =
new CameraIdAndSessionConfiguration[size];
@@ -1436,10 +1436,10 @@
/**
* Helper function to find out if a camera id is in the set of combinations returned by
- * getConcurrentStreamingCameraIds()
+ * getConcurrentCameraIds()
* @param cameraId the unique identifier of the camera device to query
* @return Whether the camera device was found in the set of combinations returned by
- * getConcurrentStreamingCameraIds
+ * getConcurrentCameraIds
*/
public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) {
if (!mDeviceStatus.containsKey(cameraId)) {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 8f3cb93..45d6fed 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1066,8 +1066,9 @@
* <li>One Jpeg ImageReader, any resolution: the camera device is
* allowed to slow down JPEG output speed by 50% if there is any ongoing offline
* session.</li>
- * <li>One ImageWriter surface of private format, any resolution if the device supports
- * PRIVATE_REPROCESSING capability</li>
+ * <li>If the device supports PRIVATE_REPROCESSING, one pair of ImageWriter/ImageReader
+ * surfaces of private format, with the same resolution that is larger or equal to
+ * the JPEG ImageReader resolution above.</li>
* </ol>
* </li>
* <li>Alternatively, the active camera session above can be replaced by an legacy
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index f0fab6a..20d9c30 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -721,7 +721,7 @@
/**
* Retrieve a list of all available mandatory concurrent stream combinations.
* This method should only be called for devices which are listed in combinations returned
- * by CameraManager.getConcurrentStreamingCameraIds.
+ * by CameraManager.getConcurrentCameraIds.
*
* @return a non-modifiable list of supported mandatory concurrent stream combinations.
*/
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 1b6c1ee..7e4d68d 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -20,6 +20,7 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -71,6 +72,7 @@
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
+import android.view.WindowInsets.Side;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillId;
@@ -1246,7 +1248,8 @@
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
- mWindow.getWindow().getAttributes().setFitInsetsTypes(WindowInsets.Type.statusBars());
+ mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
+ mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
// IME layout should always be inset by navigation bar, no matter its current visibility,
// unless automotive requests it, since automotive may hide the navigation bar.
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index d009144..6f0a4f9 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -136,7 +136,7 @@
* {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
* {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
*
- * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED
+ * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED
*/
@NetworkValidationResult
public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
@@ -233,8 +233,8 @@
* Constructor for ConnectivityReport.
*
* <p>Apps should obtain instances through {@link
- * ConnectivityDiagnosticsCallback#onConnectivityReport} instead of instantiating their own
- * instances (unless for testing purposes).
+ * ConnectivityDiagnosticsCallback#onConnectivityReportAvailable} instead of instantiating
+ * their own instances (unless for testing purposes).
*
* @param network The Network for which this ConnectivityReport applies
* @param reportTimestamp The timestamp for the report
@@ -368,7 +368,14 @@
/** Class that includes information for a suspected data stall on a specific Network */
public static final class DataStallReport implements Parcelable {
+ /**
+ * Indicates that the Data Stall was detected using DNS events.
+ */
public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+
+ /**
+ * Indicates that the Data Stall was detected using TCP metrics.
+ */
public static final int DETECTION_METHOD_TCP_METRICS = 2;
/** @hide */
@@ -615,10 +622,10 @@
/** @hide */
@VisibleForTesting
- public void onConnectivityReport(@NonNull ConnectivityReport report) {
+ public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
Binder.withCleanCallingIdentity(() -> {
mExecutor.execute(() -> {
- mCb.onConnectivityReport(report);
+ mCb.onConnectivityReportAvailable(report);
});
});
}
@@ -659,7 +666,7 @@
*
* @param report The ConnectivityReport containing information about a connectivity check
*/
- public void onConnectivityReport(@NonNull ConnectivityReport report) {}
+ public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {}
/**
* Called when the platform suspects a data stall on some Network.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fc6954f..81735ac 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3222,7 +3222,9 @@
/** {@hide} - returns the factory serial number */
@UnsupportedAppUsage
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public int registerNetworkFactory(Messenger messenger, String name) {
try {
return mService.registerNetworkFactory(messenger, name);
@@ -3233,7 +3235,9 @@
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public void unregisterNetworkFactory(Messenger messenger) {
try {
mService.unregisterNetworkFactory(messenger);
@@ -3253,7 +3257,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public int registerNetworkProvider(@NonNull NetworkProvider provider) {
if (provider.getProviderId() != NetworkProvider.ID_NONE) {
throw new IllegalStateException("NetworkProviders can only be registered once");
@@ -3276,7 +3282,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public void unregisterNetworkProvider(@NonNull NetworkProvider provider) {
try {
mService.unregisterNetworkProvider(provider.getMessenger());
@@ -3288,7 +3296,9 @@
/** @hide exposed via the NetworkProvider class. */
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
try {
mService.declareNetworkRequestUnfulfillable(request);
@@ -3306,7 +3316,9 @@
* Register a NetworkAgent with ConnectivityService.
* @return Network corresponding to NetworkAgent.
*/
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
NetworkCapabilities nc, int score, NetworkAgentConfig config) {
return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
@@ -3317,7 +3329,9 @@
* Register a NetworkAgent with ConnectivityService.
* @return Network corresponding to NetworkAgent.
*/
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
try {
diff --git a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
index 3a161bf..82b64a9 100644
--- a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
+++ b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
@@ -22,7 +22,7 @@
/** @hide */
oneway interface IConnectivityDiagnosticsCallback {
- void onConnectivityReport(in ConnectivityDiagnosticsManager.ConnectivityReport report);
+ void onConnectivityReportAvailable(in ConnectivityDiagnosticsManager.ConnectivityReport report);
void onDataStallSuspected(in ConnectivityDiagnosticsManager.DataStallReport report);
void onNetworkConnectivityReported(in Network n, boolean hasConnectivity);
}
\ No newline at end of file
diff --git a/core/java/android/net/ITestNetworkManager.aidl b/core/java/android/net/ITestNetworkManager.aidl
index d586038..2a863ad 100644
--- a/core/java/android/net/ITestNetworkManager.aidl
+++ b/core/java/android/net/ITestNetworkManager.aidl
@@ -33,7 +33,7 @@
TestNetworkInterface createTapInterface();
void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
- in IBinder binder);
+ in int[] administratorUids, in IBinder binder);
void teardownTestNetwork(int netId);
}
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index 2b8b7e6..6c0ba2f 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.net.util.IpUtils;
-import android.os.Parcel;
import android.util.Log;
import java.net.InetAddress;
@@ -30,7 +29,6 @@
/**
* Represents the actual packets that are sent by the
* {@link android.net.SocketKeepalive} API.
- *
* @hide
*/
@SystemApi
@@ -54,6 +52,9 @@
/** Packet data. A raw byte string of packet data, not including the link-layer header. */
private final byte[] mPacket;
+ // Note: If you add new fields, please modify the parcelling code in the child classes.
+
+
// This should only be constructed via static factory methods, such as
// nattKeepalivePacket.
/**
@@ -87,21 +88,4 @@
return mPacket.clone();
}
- /** @hide */
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(srcAddress.getHostAddress());
- out.writeString(dstAddress.getHostAddress());
- out.writeInt(srcPort);
- out.writeInt(dstPort);
- out.writeByteArray(mPacket);
- }
-
- /** @hide */
- protected KeepalivePacketData(Parcel in) {
- srcAddress = NetworkUtils.numericToInetAddress(in.readString());
- dstAddress = NetworkUtils.numericToInetAddress(in.readString());
- srcPort = in.readInt();
- dstPort = in.readInt();
- mPacket = in.createByteArray();
- }
}
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
index bd39c13..29da495 100644
--- a/core/java/android/net/NattKeepalivePacketData.java
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -20,6 +20,7 @@
import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.util.IpUtils;
import android.os.Parcel;
@@ -30,6 +31,7 @@
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Objects;
/** @hide */
@SystemApi
@@ -121,4 +123,19 @@
return new NattKeepalivePacketData[size];
}
};
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof NattKeepalivePacketData)) return false;
+ final NattKeepalivePacketData other = (NattKeepalivePacketData) o;
+ return this.srcAddress.equals(other.srcAddress)
+ && this.dstAddress.equals(other.dstAddress)
+ && this.srcPort == other.srcPort
+ && this.dstPort == other.dstPort;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(srcAddress, dstAddress, srcPort, dstPort);
+ }
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index c5681cb..6f5471b 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -61,6 +61,7 @@
public class Network implements Parcelable {
/**
+ * The unique id of the network.
* @hide
*/
@SystemApi
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index fef353f..5c754a1 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -78,6 +78,7 @@
/**
* The ID of the {@link NetworkProvider} that created this object, or
* {@link NetworkProvider#ID_NONE} if unknown.
+ * @hide
*/
public final int providerId;
@@ -584,6 +585,7 @@
*
* @deprecated this is for backward compatibility only.
* @param legacySubtype the legacy subtype.
+ * @hide
*/
@Deprecated
public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
@@ -608,6 +610,7 @@
*
* @deprecated this is for backward compatibility only.
* @param extraInfo the ExtraInfo.
+ * @hide
*/
@Deprecated
public void setLegacyExtraInfo(@Nullable final String extraInfo) {
@@ -711,6 +714,7 @@
/**
* Called when ConnectivityService request a bandwidth update. The parent factory
* shall try to overwrite this method and produce a bandwidth update if capable.
+ * @hide
*/
public void onBandwidthUpdateRequested() {
pollLceData();
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index 7e2db4a..ca9328a 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -108,6 +108,7 @@
/**
*
* @return whether the sign in to network notification is enabled by this configuration.
+ * @hide
*/
public boolean isProvisioningNotificationEnabled() {
return !provisioningNotificationDisabled;
@@ -122,6 +123,7 @@
/**
* @return the subscriber ID, or null if none.
+ * @hide
*/
@Nullable
public String getSubscriberId() {
@@ -138,6 +140,7 @@
/**
* @return whether NAT64 prefix detection is enabled.
+ * @hide
*/
public boolean isNat64DetectionEnabled() {
return !skip464xlat;
@@ -247,6 +250,7 @@
* Sets the subscriber ID for this network.
*
* @return this builder, to facilitate chaining.
+ * @hide
*/
@NonNull
public Builder setSubscriberId(@Nullable String subscriberId) {
@@ -259,6 +263,7 @@
* and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
*
* @return this builder, to facilitate chaining.
+ * @hide
*/
@NonNull
public Builder disableNat64Detection() {
@@ -271,6 +276,7 @@
* perform its own carrier-specific provisioning procedure.
*
* @return this builder, to facilitate chaining.
+ * @hide
*/
@NonNull
public Builder disableProvisioningNotification() {
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 83f9980..116e343 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -613,7 +613,6 @@
* @return {@code true} if the network should be restricted.
* @hide
*/
- @SystemApi
public boolean deduceRestrictedCapability() {
// Check if we have any capability that forces the network to be restricted.
final boolean forceRestrictedCapability =
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 14442a2..1922b6d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -21,7 +21,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -56,7 +55,6 @@
* @hide
*/
@SystemService(Context.NETWORK_POLICY_SERVICE)
-@SystemApi
public class NetworkPolicyManager {
/* POLICY_* are masks and can be ORed, although currently they are not.*/
@@ -162,11 +160,13 @@
/**
* Mask used to check if an override value is marked as unmetered.
+ * @hide
*/
public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1 << 0;
/**
* Mask used to check if an override value is marked as congested.
+ * @hide
*/
public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 1 << 1;
@@ -294,7 +294,6 @@
/** @hide */
@RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
- @SystemApi
public void registerSubscriptionCallback(@NonNull SubscriptionCallback callback) {
if (callback == null) {
throw new NullPointerException("Callback cannot be null.");
@@ -309,7 +308,6 @@
/** @hide */
@RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
- @SystemApi
public void unregisterSubscriptionCallback(@NonNull SubscriptionCallback callback) {
if (callback == null) {
throw new NullPointerException("Callback cannot be null.");
@@ -373,6 +371,7 @@
* requested state until explicitly cleared, or the next reboot,
* whichever happens first
* @param callingPackage the name of the package making the call.
+ * @hide
*/
public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
@SubscriptionOverrideMask int overrideValue, long timeoutMillis,
@@ -391,6 +390,7 @@
* @param subId the subscriber this relationship applies to.
* @param plans the list of plans.
* @param callingPackage the name of the package making the call
+ * @hide
*/
public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans,
@NonNull String callingPackage) {
@@ -406,6 +406,7 @@
*
* @param subId the subscriber to get the subscription plans for.
* @param callingPackage the name of the package making the call.
+ * @hide
*/
@NonNull
public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) {
@@ -549,7 +550,6 @@
}
/** @hide */
- @SystemApi
public static class SubscriptionCallback {
/**
* Notify clients of a new override about a given subscription.
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index 2c0e4aa..418d691 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -106,10 +106,12 @@
}
// TODO: consider adding a register() method so ConnectivityManager does not need to call this.
+ /** @hide */
public @Nullable Messenger getMessenger() {
return mMessenger;
}
+ /** @hide */
public @NonNull String getName() {
return mName;
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 2f536ff..9c1fb41 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -21,7 +21,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
@@ -58,9 +57,12 @@
public final class NetworkStats implements Parcelable {
private static final String TAG = "NetworkStats";
- /** {@link #iface} value when interface details unavailable. */
- @SuppressLint("CompileTimeConstant")
+ /**
+ * {@link #iface} value when interface details unavailable.
+ * @hide
+ */
@Nullable public static final String IFACE_ALL = null;
+
/**
* Virtual network interface for video telephony. This is for VT data usage counting
* purpose.
@@ -248,7 +250,13 @@
@UnsupportedAppUsage
private long[] operations;
- /** @hide */
+ /**
+ * Basic element of network statistics. Contains the number of packets and number of bytes
+ * transferred on both directions in a given set of conditions. See
+ * {@link Entry#Entry(String, int, int, int, int, int, int, long, long, long, long, long)}.
+ *
+ * @hide
+ */
@SystemApi
public static class Entry {
/** @hide */
@@ -319,6 +327,35 @@
rxBytes, rxPackets, txBytes, txPackets, operations);
}
+ /**
+ * Construct a {@link Entry} object by giving statistics of packet and byte transferred on
+ * both direction, and associated with a set of given conditions.
+ *
+ * @param iface interface name of this {@link Entry}. Or null if not specified.
+ * @param uid uid of this {@link Entry}. {@link #UID_TETHERING} if this {@link Entry} is
+ * for tethering. Or {@link #UID_ALL} if this {@link NetworkStats} is only
+ * counting iface stats.
+ * @param set usage state of this {@link Entry}. Should be one of the following
+ * values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
+ * @param tag tag of this {@link Entry}.
+ * @param metered metered state of this {@link Entry}. Should be one of the following
+ * values: {link #METERED_YES}, {link #METERED_NO}.
+ * @param roaming roaming state of this {@link Entry}. Should be one of the following
+ * values: {link #ROAMING_YES}, {link #ROAMING_NO}.
+ * @param defaultNetwork default network status of this {@link Entry}. Should be one
+ * of the following values: {link #DEFAULT_NETWORK_YES},
+ * {link #DEFAULT_NETWORK_NO}.
+ * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param operations count of network operations performed for this {@link Entry}. This can
+ * be used to derive bytes-per-operation.
+ */
public Entry(@Nullable String iface, int uid, @State int set, int tag,
@Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork,
long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
@@ -466,7 +503,7 @@
NetworkStats.Entry entry = null;
for (int i = 0; i < size; i++) {
entry = getValues(i, entry);
- clone.addEntry(entry);
+ clone.insertEntry(entry);
}
return clone;
}
@@ -493,26 +530,26 @@
/** @hide */
@VisibleForTesting
- public NetworkStats addIfaceValues(
+ public NetworkStats insertEntry(
String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- return addEntry(
+ return insertEntry(
iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
}
/** @hide */
@VisibleForTesting
- public NetworkStats addEntry(String iface, int uid, int set, int tag, long rxBytes,
+ public NetworkStats insertEntry(String iface, int uid, int set, int tag, long rxBytes,
long rxPackets, long txBytes, long txPackets, long operations) {
- return addEntry(new Entry(
+ return insertEntry(new Entry(
iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
}
/** @hide */
@VisibleForTesting
- public NetworkStats addEntry(String iface, int uid, int set, int tag, int metered, int roaming,
- int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
- long operations) {
- return addEntry(new Entry(
+ public NetworkStats insertEntry(String iface, int uid, int set, int tag, int metered,
+ int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes,
+ long txPackets, long operations) {
+ return insertEntry(new Entry(
iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
txBytes, txPackets, operations));
}
@@ -522,7 +559,7 @@
* object can be recycled across multiple calls.
* @hide
*/
- public NetworkStats addEntry(Entry entry) {
+ public NetworkStats insertEntry(Entry entry) {
if (size >= capacity) {
final int newLength = Math.max(size, 10) * 3 / 2;
iface = Arrays.copyOf(iface, newLength);
@@ -665,7 +702,7 @@
entry.roaming, entry.defaultNetwork);
if (i == -1) {
// only create new entry when positive contribution
- addEntry(entry);
+ insertEntry(entry);
} else {
rxBytes[i] += entry.rxBytes;
rxPackets[i] += entry.rxPackets;
@@ -684,7 +721,7 @@
* @param entry the {@link Entry} to add.
* @return a new constructed {@link NetworkStats} object that contains the result.
*/
- public @NonNull NetworkStats addValues(@NonNull Entry entry) {
+ public @NonNull NetworkStats addEntry(@NonNull Entry entry) {
return this.clone().combineValues(entry);
}
@@ -1003,7 +1040,7 @@
entry.operations = Math.max(entry.operations, 0);
}
- result.addEntry(entry);
+ result.insertEntry(entry);
}
return result;
diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java
index 4ac4a69..c3284df 100644
--- a/core/java/android/net/TestNetworkManager.java
+++ b/core/java/android/net/TestNetworkManager.java
@@ -16,6 +16,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.IBinder;
import android.os.RemoteException;
@@ -53,6 +54,19 @@
}
}
+ private void setupTestNetwork(
+ @NonNull String iface,
+ @Nullable LinkProperties lp,
+ boolean isMetered,
+ @NonNull int[] administratorUids,
+ @NonNull IBinder binder) {
+ try {
+ mService.setupTestNetwork(iface, lp, isMetered, administratorUids, binder);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Sets up a capability-limited, testing-only network for a given interface
*
@@ -66,11 +80,7 @@
public void setupTestNetwork(
@NonNull LinkProperties lp, boolean isMetered, @NonNull IBinder binder) {
Preconditions.checkNotNull(lp, "Invalid LinkProperties");
- try {
- mService.setupTestNetwork(lp.getInterfaceName(), lp, isMetered, binder);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setupTestNetwork(lp.getInterfaceName(), lp, isMetered, new int[0], binder);
}
/**
@@ -82,11 +92,21 @@
*/
@TestApi
public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
- try {
- mService.setupTestNetwork(iface, null, true, binder);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setupTestNetwork(iface, null, true, new int[0], binder);
+ }
+
+ /**
+ * Sets up a capability-limited, testing-only network for a given interface with the given
+ * administrator UIDs.
+ *
+ * @param iface the name of the interface to be used for the Network LinkProperties.
+ * @param administratorUids The administrator UIDs to be used for the test-only network
+ * @param binder A binder object guarding the lifecycle of this test network.
+ * @hide
+ */
+ public void setupTestNetwork(
+ @NonNull String iface, @NonNull int[] administratorUids, @NonNull IBinder binder) {
+ setupTestNetwork(iface, null, true, administratorUids, binder);
}
/**
diff --git a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java b/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
deleted file mode 100644
index 740aa92..0000000
--- a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.net.NetworkStats;
-
-/**
- * A base class that allows external modules to implement a custom network statistics provider.
- * @hide
- */
-@SystemApi
-public abstract class AbstractNetworkStatsProvider {
- /**
- * A value used by {@link #setLimit} and {@link #setAlert} indicates there is no limit.
- */
- public static final int QUOTA_UNLIMITED = -1;
-
- /**
- * Called by {@code NetworkStatsService} when global polling is needed. Custom
- * implementation of providers MUST respond to it by calling
- * {@link NetworkStatsProviderCallback#onStatsUpdated} within one minute. Responding
- * later than this may cause the stats to be dropped.
- *
- * @param token a positive number identifying the new state of the system under which
- * {@link NetworkStats} have to be gathered from now on. When this is called,
- * custom implementations of providers MUST report the latest stats with the
- * previous token, under which stats were being gathered so far.
- */
- public abstract void requestStatsUpdate(int token);
-
- /**
- * Called by {@code NetworkStatsService} when setting the interface quota for the specified
- * upstream interface. When this is called, the custom implementation should block all egress
- * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
- * been reached, and MUST respond to it by calling
- * {@link NetworkStatsProviderCallback#onLimitReached()}.
- *
- * @param iface the interface requiring the operation.
- * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
- * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
- */
- public abstract void setLimit(@NonNull String iface, long quotaBytes);
-
- /**
- * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
- * MUST call {@link NetworkStatsProviderCallback#onAlertReached()} when {@code quotaBytes} bytes
- * have been reached. Unlike {@link #setLimit(String, long)}, the custom implementation should
- * not block all egress packets.
- *
- * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
- * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
- */
- public abstract void setAlert(long quotaBytes);
-}
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
index 55b3d4e..4078b24 100644
--- a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
+++ b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
@@ -22,7 +22,7 @@
* @hide
*/
oneway interface INetworkStatsProvider {
- void requestStatsUpdate(int token);
- void setLimit(String iface, long quotaBytes);
- void setAlert(long quotaBytes);
+ void onRequestStatsUpdate(int token);
+ void onSetLimit(String iface, long quotaBytes);
+ void onSetAlert(long quotaBytes);
}
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
index 3ea9318..bd336dd 100644
--- a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
+++ b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
@@ -24,8 +24,8 @@
* @hide
*/
oneway interface INetworkStatsProviderCallback {
- void onStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
- void onAlertReached();
- void onLimitReached();
+ void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
+ void notifyAlertReached();
+ void notifyLimitReached();
void unregister();
}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProvider.java b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
new file mode 100644
index 0000000..7639d22
--- /dev/null
+++ b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
@@ -0,0 +1,195 @@
+/*
+ * 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 android.net.netstats.provider;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.NetworkStats;
+import android.os.RemoteException;
+
+/**
+ * A base class that allows external modules to implement a custom network statistics provider.
+ * @hide
+ */
+@SystemApi
+public abstract class NetworkStatsProvider {
+ /**
+ * A value used by {@link #onSetLimit} and {@link #onSetAlert} indicates there is no limit.
+ */
+ public static final int QUOTA_UNLIMITED = -1;
+
+ @NonNull private final INetworkStatsProvider mProviderBinder =
+ new INetworkStatsProvider.Stub() {
+
+ @Override
+ public void onRequestStatsUpdate(int token) {
+ NetworkStatsProvider.this.onRequestStatsUpdate(token);
+ }
+
+ @Override
+ public void onSetLimit(String iface, long quotaBytes) {
+ NetworkStatsProvider.this.onSetLimit(iface, quotaBytes);
+ }
+
+ @Override
+ public void onSetAlert(long quotaBytes) {
+ NetworkStatsProvider.this.onSetAlert(quotaBytes);
+ }
+ };
+
+ // The binder given by the service when successfully registering. Only null before registering,
+ // never null once non-null.
+ @Nullable
+ private INetworkStatsProviderCallback mProviderCbBinder;
+
+ /**
+ * Return the binder invoked by the service and redirect function calls to the overridden
+ * methods.
+ * @hide
+ */
+ @NonNull
+ public INetworkStatsProvider getProviderBinder() {
+ return mProviderBinder;
+ }
+
+ /**
+ * Store the binder that was returned by the service when successfully registering. Note that
+ * the provider cannot be re-registered. Hence this method can only be called once per provider.
+ *
+ * @hide
+ */
+ public void setProviderCallbackBinder(@NonNull INetworkStatsProviderCallback binder) {
+ if (mProviderCbBinder != null) {
+ throw new IllegalArgumentException("provider is already registered");
+ }
+ mProviderCbBinder = binder;
+ }
+
+ /**
+ * Get the binder that was returned by the service when successfully registering. Or null if the
+ * provider was never registered.
+ *
+ * @hide
+ */
+ @Nullable
+ public INetworkStatsProviderCallback getProviderCallbackBinder() {
+ return mProviderCbBinder;
+ }
+
+ /**
+ * Get the binder that was returned by the service when successfully registering. Throw an
+ * {@link IllegalStateException} if the provider is not registered.
+ *
+ * @hide
+ */
+ @NonNull
+ public INetworkStatsProviderCallback getProviderCallbackBinderOrThrow() {
+ if (mProviderCbBinder == null) {
+ throw new IllegalStateException("the provider is not registered");
+ }
+ return mProviderCbBinder;
+ }
+
+ /**
+ * Notify the system of new network statistics.
+ *
+ * Send the network statistics recorded since the last call to {@link #notifyStatsUpdated}. Must
+ * be called as soon as possible after {@link NetworkStatsProvider#onRequestStatsUpdate(int)}
+ * being called. Responding later increases the probability stats will be dropped. The
+ * provider can also call this whenever it wants to reports new stats for any reason.
+ * Note that the system will not necessarily immediately propagate the statistics to
+ * reflect the update.
+ *
+ * @param token the token under which these stats were gathered. Providers can call this method
+ * with the current token as often as they want, until the token changes.
+ * {@see NetworkStatsProvider#onRequestStatsUpdate()}
+ * @param ifaceStats the {@link NetworkStats} per interface to be reported.
+ * The provider should not include any traffic that is already counted by
+ * kernel interface counters.
+ * @param uidStats the same stats as above, but counts {@link NetworkStats}
+ * per uid.
+ */
+ public void notifyStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
+ @NonNull NetworkStats uidStats) {
+ try {
+ getProviderCallbackBinderOrThrow().notifyStatsUpdated(token, ifaceStats, uidStats);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Notify system that the quota set by {@code onSetAlert} has been reached.
+ */
+ public void notifyAlertReached() {
+ try {
+ getProviderCallbackBinderOrThrow().notifyAlertReached();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Notify system that the quota set by {@code onSetLimit} has been reached.
+ */
+ public void notifyLimitReached() {
+ try {
+ getProviderCallbackBinderOrThrow().notifyLimitReached();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Called by {@code NetworkStatsService} when it requires to know updated stats.
+ * The provider MUST respond by calling {@link #notifyStatsUpdated} as soon as possible.
+ * Responding later increases the probability stats will be dropped. Memory allowing, the
+ * system will try to take stats into account up to one minute after calling
+ * {@link #onRequestStatsUpdate}.
+ *
+ * @param token a positive number identifying the new state of the system under which
+ * {@link NetworkStats} have to be gathered from now on. When this is called,
+ * custom implementations of providers MUST tally and report the latest stats with
+ * the previous token, under which stats were being gathered so far.
+ */
+ public abstract void onRequestStatsUpdate(int token);
+
+ /**
+ * Called by {@code NetworkStatsService} when setting the interface quota for the specified
+ * upstream interface. When this is called, the custom implementation should block all egress
+ * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
+ * been reached, and MUST respond to it by calling
+ * {@link NetworkStatsProvider#notifyLimitReached()}.
+ *
+ * @param iface the interface requiring the operation.
+ * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+ * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
+ */
+ public abstract void onSetLimit(@NonNull String iface, long quotaBytes);
+
+ /**
+ * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
+ * MUST call {@link NetworkStatsProvider#notifyAlertReached()} when {@code quotaBytes} bytes
+ * have been reached. Unlike {@link #onSetLimit(String, long)}, the custom implementation should
+ * not block all egress packets.
+ *
+ * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+ * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
+ */
+ public abstract void onSetAlert(long quotaBytes);
+}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java b/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
deleted file mode 100644
index e17a8ee..0000000
--- a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.net.NetworkStats;
-import android.os.RemoteException;
-
-/**
- * A callback class that allows callers to report events to the system.
- * @hide
- */
-@SystemApi
-@SuppressLint("CallbackMethodName")
-public class NetworkStatsProviderCallback {
- @NonNull private final INetworkStatsProviderCallback mBinder;
-
- /** @hide */
- public NetworkStatsProviderCallback(@NonNull INetworkStatsProviderCallback binder) {
- mBinder = binder;
- }
-
- /**
- * Notify the system of new network statistics.
- *
- * Send the network statistics recorded since the last call to {@link #onStatsUpdated}. Must be
- * called within one minute of {@link AbstractNetworkStatsProvider#requestStatsUpdate(int)}
- * being called. The provider can also call this whenever it wants to reports new stats for any
- * reason. Note that the system will not necessarily immediately propagate the statistics to
- * reflect the update.
- *
- * @param token the token under which these stats were gathered. Providers can call this method
- * with the current token as often as they want, until the token changes.
- * {@see AbstractNetworkStatsProvider#requestStatsUpdate()}
- * @param ifaceStats the {@link NetworkStats} per interface to be reported.
- * The provider should not include any traffic that is already counted by
- * kernel interface counters.
- * @param uidStats the same stats as above, but counts {@link NetworkStats}
- * per uid.
- */
- public void onStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
- @NonNull NetworkStats uidStats) {
- try {
- mBinder.onStatsUpdated(token, ifaceStats, uidStats);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Notify system that the quota set by {@code setAlert} has been reached.
- */
- public void onAlertReached() {
- try {
- mBinder.onAlertReached();
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Notify system that the quota set by {@code setLimit} has been reached.
- */
- public void onLimitReached() {
- try {
- mBinder.onLimitReached();
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Unregister the provider and the referencing callback.
- */
- public void unregister() {
- try {
- mBinder.unregister();
- } catch (RemoteException e) {
- // Ignore error.
- }
- }
-}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java b/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
deleted file mode 100644
index 4bf7c9b..0000000
--- a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-
-/**
- * A wrapper class of {@link INetworkStatsProvider} that hides the binder interface from exposing
- * to outer world.
- *
- * @hide
- */
-public class NetworkStatsProviderWrapper extends INetworkStatsProvider.Stub {
- @NonNull final AbstractNetworkStatsProvider mProvider;
-
- public NetworkStatsProviderWrapper(AbstractNetworkStatsProvider provider) {
- mProvider = provider;
- }
-
- @Override
- public void requestStatsUpdate(int token) {
- mProvider.requestStatsUpdate(token);
- }
-
- @Override
- public void setLimit(@NonNull String iface, long quotaBytes) {
- mProvider.setLimit(iface, quotaBytes);
- }
-
- @Override
- public void setAlert(long quotaBytes) {
- mProvider.setAlert(quotaBytes);
- }
-}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index d320f61..c61f10f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -245,13 +245,25 @@
/**
* Mandatory String extra field in {@link #ACTION_PREFERRED_PAYMENT_CHANGED}
- * Indicates the condition when trigger this event.
+ * Indicates the condition when trigger this event. Possible values are:
+ * {@link #PREFERRED_PAYMENT_LOADED},
+ * {@link #PREFERRED_PAYMENT_CHANGED},
+ * {@link #PREFERRED_PAYMENT_UPDATED},
*/
public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON =
"android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON";
-
+ /**
+ * Nfc is enabled and the preferred payment aids are registered.
+ */
public static final int PREFERRED_PAYMENT_LOADED = 1;
+ /**
+ * User selected another payment application as the preferred payment.
+ */
public static final int PREFERRED_PAYMENT_CHANGED = 2;
+ /**
+ * Current preferred payment has issued an update (registered/unregistered new aids or has been
+ * updated itself).
+ */
public static final int PREFERRED_PAYMENT_UPDATED = 3;
public static final int STATE_OFF = 1;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index f1c74a6..7bf82e8 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -672,7 +672,7 @@
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
- return null;
+ throw e.rethrowFromSystemServer();
}
try {
ApduServiceInfo serviceInfo =
@@ -680,7 +680,7 @@
return (serviceInfo != null ? serviceInfo.getAids() : null);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover CardEmulationService.");
- return null;
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -690,9 +690,16 @@
*
* @return The route destination secure element name of the preferred payment service.
* HCE payment: "Host"
- * OffHost payment: prefix SIM or prefix eSE string.
- * "OffHost" if the payment service does not specify secure element
- * name.
+ * OffHost payment: 1. String with prefix SIM or prefix eSE string.
+ * Ref: GSMA TS.26 - NFC Handset Requirements
+ * TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be
+ * SIM[smartcard slot]
+ * (e.g. SIM/SIM1, SIM2… SIMn).
+ * TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be
+ * eSE[number]
+ * (e.g. eSE/eSE1, eSE2, etc.).
+ * 2. "OffHost" if the payment service does not specify secure element
+ * name.
*/
@RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
@Nullable
@@ -711,7 +718,7 @@
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
- return null;
+ throw e.rethrowFromSystemServer();
}
try {
ApduServiceInfo serviceInfo =
@@ -727,7 +734,7 @@
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover CardEmulationService.");
- return null;
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -739,7 +746,7 @@
*/
@RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
@Nullable
- public String getDescriptionForPreferredPaymentService() {
+ public CharSequence getDescriptionForPreferredPaymentService() {
try {
ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(mContext.getUserId());
return (serviceInfo != null ? serviceInfo.getDescription() : null);
@@ -747,7 +754,7 @@
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
- return null;
+ throw e.rethrowFromSystemServer();
}
try {
ApduServiceInfo serviceInfo =
@@ -755,7 +762,7 @@
return (serviceInfo != null ? serviceInfo.getDescription() : null);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover CardEmulationService.");
- return null;
+ throw e.rethrowFromSystemServer();
}
}
}
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 9ca1c33..fab906b 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -157,20 +157,20 @@
Preconditions.checkNotNull(executor);
Preconditions.checkNotNull(callback);
- boolean validScreenshotFd = screenshotFd != null;
+ boolean isScreenshotRequested = screenshotFd != null;
if (screenshotFd == null) {
// Binder needs a valid File Descriptor to be passed
screenshotFd = ParcelFileDescriptor.open(new File("/dev/null"),
ParcelFileDescriptor.MODE_READ_ONLY);
}
DumpstateListener dsListener = new DumpstateListener(executor, callback,
- validScreenshotFd);
+ isScreenshotRequested);
// Note: mBinder can get callingUid from the binder transaction.
mBinder.startBugreport(-1 /* callingUid */,
mContext.getOpPackageName(),
bugreportFd.getFileDescriptor(),
screenshotFd.getFileDescriptor(),
- params.getMode(), dsListener);
+ params.getMode(), dsListener, isScreenshotRequested);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (FileNotFoundException e) {
@@ -225,13 +225,13 @@
private final class DumpstateListener extends IDumpstateListener.Stub {
private final Executor mExecutor;
private final BugreportCallback mCallback;
- private final boolean mValidScreenshotFd;
+ private final boolean mIsScreenshotRequested;
DumpstateListener(Executor executor, BugreportCallback callback,
- boolean validScreenshotFd) {
+ boolean isScreenshotRequested) {
mExecutor = executor;
mCallback = callback;
- mValidScreenshotFd = validScreenshotFd;
+ mIsScreenshotRequested = isScreenshotRequested;
}
@Override
@@ -272,7 +272,7 @@
@Override
public void onScreenshotTaken(boolean success) throws RemoteException {
- if (!mValidScreenshotFd) {
+ if (!mIsScreenshotRequested) {
return;
}
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 9c999b2..a28f5fb 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -16,6 +16,9 @@
package android.os;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
/**
@@ -114,20 +117,37 @@
= "android.os.action.UPDATE_CARRIER_ID_DB";
/**
- * Broadcast intent action indicating that the updated emergency number database is available.
- * <p>Extra: "VERSION" the numeric version of the new data. Devices should only install if the
- * update version is newer than the current one.
- * <p>Extra: "REQUIRED_HASH" the hash of the current update data.
- * <p>Input: {@link android.content.Intent#getData} is URI of downloaded emergency number file.
- * Devices should pick up the downloaded file and persist to the database
- * {@code com.android.internal.telephony.emergency.EmergencyNumberTracker}.
+ * Update the emergency number database into the devices.
+ * <p>Extra: {@link #EXTRA_VERSION} the numeric version of the database.
+ * <p>Extra: {@link #EXTRA_REQUIRED_HASH} the hash of the database.
+ * <p>Input: {@link android.content.Intent#getData} the URI to download emergency number
+ * database.
*
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.UPDATE_CONFIG)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB =
"android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
+ /**
+ * An integer to indicate the numeric version of the new data. Devices should only install
+ * if the update version is newer than the current one.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_VERSION = "android.os.extra.VERSION";
+
+ /**
+ * A string to indicate the hash of the data.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH";
+
private ConfigUpdate() {
}
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index f2fb5b2..ae65f1d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -23,6 +23,9 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -88,6 +91,46 @@
private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT,
"/apex");
+ /**
+ * Scoped Storage is on by default. However, it is not strictly enforced and there are multiple
+ * ways to opt out of scoped storage:
+ * <ul>
+ * <li>Target Sdk < Q</li>
+ * <li>Target Sdk = Q and has `requestLegacyExternalStorage` set in AndroidManifest.xml</li>
+ * <li>Target Sdk > Q: Upgrading from an app that was opted out of scoped storage and has
+ * `preserveLegacyExternalStorage` set in AndroidManifest.xml</li>
+ * </ul>
+ * This flag is enabled for all apps by default as Scoped Storage is enabled by default.
+ * Developers can disable this flag to opt out of Scoped Storage and have legacy storage
+ * workflow.
+ *
+ * Note: {@code FORCE_ENABLE_SCOPED_STORAGE} should also be disabled for apps to opt out of
+ * scoped storage.
+ * Note: This flag is also used in {@code com.android.providers.media.LocalCallingIdentity}.
+ * Any modifications to this flag should be reflected there as well.
+ * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+ */
+ @ChangeId
+ private static final long DEFAULT_SCOPED_STORAGE = 149924527L;
+
+ /**
+ * Setting this flag strictly enforces Scoped Storage regardless of:
+ * <ul>
+ * <li>The value of Target Sdk</li>
+ * <li>The value of `requestLegacyExternalStorage` in AndroidManifest.xml</li>
+ * <li>The value of `preserveLegacyExternalStorage` in AndroidManifest.xml</li>
+ * </ul>
+ *
+ * Note: {@code DEFAULT_SCOPED_STORAGE} should also be enabled for apps to be enforced into
+ * scoped storage.
+ * Note: This flag is also used in {@code com.android.providers.media.LocalCallingIdentity}.
+ * Any modifications to this flag should be reflected there as well.
+ * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+ */
+ @ChangeId
+ @Disabled
+ private static final long FORCE_ENABLE_SCOPED_STORAGE = 132649864L;
+
@UnsupportedAppUsage
private static UserEnvironment sCurrentUser;
private static boolean sUserRequired;
@@ -1191,12 +1234,13 @@
}
/**
- * Returns whether the primary shared/external storage media is a legacy
- * view that includes files not owned by the app.
+ * Returns whether the shared/external storage media is a
+ * legacy view that includes files not owned by the app.
* <p>
* This value may be different from the value requested by
* {@code requestLegacyExternalStorage} in the app's manifest, since an app
- * may inherit its legacy state based on when it was first installed.
+ * may inherit its legacy state based on when it was first installed, target sdk and other
+ * factors.
* <p>
* Non-legacy apps can continue to discover and read media belonging to
* other apps via {@link android.provider.MediaStore}.
@@ -1207,18 +1251,19 @@
}
/**
- * Returns whether the shared/external storage media at the given path is a
+ * Returns whether the shared/external storage media is a
* legacy view that includes files not owned by the app.
* <p>
* This value may be different from the value requested by
* {@code requestLegacyExternalStorage} in the app's manifest, since an app
- * may inherit its legacy state based on when it was first installed.
+ * may inherit its legacy state based on when it was first installed, target sdk and other
+ * factors.
* <p>
* Non-legacy apps can continue to discover and read media belonging to
* other apps via {@link android.provider.MediaStore}.
*
* @throws IllegalArgumentException if the path is not a valid storage
- * device.
+ * device.
*/
public static boolean isExternalStorageLegacy(@NonNull File path) {
final Context context = AppGlobals.getInitialApplication();
@@ -1232,24 +1277,23 @@
return false;
}
- if (packageManager.checkPermission(Manifest.permission.WRITE_MEDIA_STORAGE,
- context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+ // TODO(b/150672994): Compat flags do not override instant app and isolated process's
+ // behavior.
+ boolean defaultScopedStorage = Compatibility.isChangeEnabled(DEFAULT_SCOPED_STORAGE);
+ boolean forceEnableScopedStorage = Compatibility.isChangeEnabled(
+ FORCE_ENABLE_SCOPED_STORAGE);
+ // if Scoped Storage is strictly enforced, the app does *not* have legacy storage access
+ // Note: does not require packagename/uid as this is directly called from an app process
+ if (isScopedStorageEnforced(defaultScopedStorage, forceEnableScopedStorage)) {
+ return false;
+ }
+ // if Scoped Storage is strictly disabled, the app has legacy storage access
+ // Note: does not require packagename/uid as this is directly called from an app process
+ if (isScopedStorageDisabled(defaultScopedStorage, forceEnableScopedStorage)) {
return true;
}
- if (packageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES,
- context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- final String[] packagesForUid = packageManager.getPackagesForUid(uid);
- for (String packageName : packagesForUid) {
- if (appOps.checkOpNoThrow(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
- uid, packageName) == AppOpsManager.MODE_ALLOWED) {
- return true;
- }
- }
-
return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
}
@@ -1298,6 +1342,16 @@
}
}
+ private static boolean isScopedStorageEnforced(boolean defaultScopedStorage,
+ boolean forceEnableScopedStorage) {
+ return defaultScopedStorage && forceEnableScopedStorage;
+ }
+
+ private static boolean isScopedStorageDisabled(boolean defaultScopedStorage,
+ boolean forceEnableScopedStorage) {
+ return !defaultScopedStorage && !forceEnableScopedStorage;
+ }
+
static File getDirectory(String variableName, String defaultPath) {
String path = System.getenv(variableName);
return path == null ? new File(defaultPath) : new File(path);
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index b478dbe..a7e3263 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -77,6 +77,7 @@
@UnsupportedAppUsage
final MessageQueue mQueue;
final Thread mThread;
+ private boolean mInLoop;
@UnsupportedAppUsage
private Printer mLogging;
@@ -155,6 +156,12 @@
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
+ if (me.mInLoop) {
+ Slog.w(TAG, "Loop again would have the queued messages be executed"
+ + " before this one completed.");
+ }
+
+ me.mInLoop = true;
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index d7af1b9..b7b3c4f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -607,6 +607,7 @@
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
+ * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
* @param zygoteArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
@@ -630,12 +631,13 @@
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, zygoteArgs);
+ pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
}
/** @hide */
@@ -659,7 +661,7 @@
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
- disabledCompatChanges, /* pkgDataInfoMap */ null, zygoteArgs);
+ disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs);
}
/**
@@ -929,6 +931,19 @@
public static final native void setProcessFrozen(int pid, int uid, boolean frozen);
/**
+ * Enable or disable the freezer. When enable == false all frozen processes are unfrozen,
+ * but aren't removed from the freezer. Processes can still be added or removed
+ * by using setProcessFrozen, but they won't actually be frozen until the freezer is enabled
+ * again. If enable == true the freezer is enabled again, and all processes
+ * in the freezer (including the ones added while the freezer was disabled) are frozen.
+ *
+ * @param enable Specify whether to enable (true) or disable (false) the freezer.
+ *
+ * @hide
+ */
+ public static final native void enableFreezer(boolean enable);
+
+ /**
* Return the scheduling group of requested process.
*
* @hide
diff --git a/core/java/android/os/Users.md b/core/java/android/os/Users.md
new file mode 100644
index 0000000..3bbbe54
--- /dev/null
+++ b/core/java/android/os/Users.md
@@ -0,0 +1,109 @@
+<!--
+ 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
+ -->
+
+# Users for system developers
+
+## Concepts
+
+### User
+
+A user of a device e.g. usually a human being. Each user has its own home screen.
+
+#### User Profile
+
+A user can have multiple profiles. E.g. one for the private life and one for work. Each profile
+has a different set of apps and accounts but they share one home screen. All profiles of a
+profile group can be active at the same time.
+
+Each profile has a separate [`userId`](#int-userid). Unless needed user profiles are treated as
+completely separate users.
+
+#### Profile Group
+
+All user profiles that share a home screen. You can list the profiles of a user via
+`UserManager#getEnabledProfiles` (you usually don't deal with disabled profiles)
+
+#### Foreground user vs background user
+
+Only a single user profile group can be in the foreground. This is the user profile the user
+currently interacts with.
+
+#### Parent user (profile)
+
+The main profile of a profile group, usually the personal (as opposed to work) profile. Get this via
+`UserManager#getProfileParent` (returns `null` if the user does not have profiles)
+
+#### Managed user (profile)
+
+The other profiles of a profile group. The name comes from the fact that these profiles are usually
+managed by a device policy controller app. You can create a managed profile from within the device
+policy controller app on your phone.
+
+#### Account
+
+An account of a user profile with a (usually internet based) service. E.g. aname@gmail.com or
+aname@yahoo.com. Each profile can have multiple accounts. A profile does not have to have a
+account.
+
+## Data types
+
+### int userId
+
+... usually marked as `@UserIdInt`
+
+The id of a user profile. List all users via `adb shell dumpsys user`. There is no data type for a
+user, all you can do is using the user id of the parent profile as a proxy for the user.
+
+### int uid
+
+Identity of an app. This is the same as a Linux uid, but in Android there is one uid per package,
+per user.
+
+It is highly discouraged, but uids can be shared between multiple packages using the
+`android:sharedUserId` manifest attribute.
+
+### class UserHandle
+
+A wrapper for userId. Used esp. in public APIs instead of `int userId` as it clearly distinguishes
+from uid.
+
+## Security model
+
+Multiple packages can share an uid by using `android:sharedUserId` manifest attribute. If packages
+share a uid they can run in the same process via `android:process` manifest attribute. Further file
+level access is also tracked by uid. Hence any security or privacy mechanism needs to be built on
+a uid granularity.
+
+On the other hand apps belonging to the same user cannot see each others files. They can only
+interact via activity launches, broadcasts, providers, and service bindings. All of them can be be
+protected by [permissions](../permission/Permissions.md). Hence any new general communication
+mechanism should be access controlled by permissions.
+
+## Lifecycle
+
+A system service should deal with users being started and stopped by overriding
+`SystemService.onSwitchUser` and `SystemService.onStopUser`.
+
+If users profiles become inactive the system should stop all apps of this profile from interacting
+with other apps or the system.
+
+Another important lifecycle event is `onUnlockUser`. Only for unlocked user profiles you can access
+all data, e.g. which packages are installed.
+
+You only want to deal with user profiles that
+
+- are in the profile group of the foreground user
+- the user profile is unlocked and not yet stopped
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 2d218f4..1992f1d 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -963,7 +963,7 @@
PRIMITIVE_QUICK_RISE,
PRIMITIVE_SLOW_RISE,
PRIMITIVE_QUICK_FALL,
- PRIMITIVE_LIGHT_TICK,
+ PRIMITIVE_TICK,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Primitive {}
@@ -981,10 +981,14 @@
* A haptic effect that simulates downwards movement with gravity. Often
* followed by extra energy of hitting and reverberation to augment
* physicality.
+ *
+ * @hide Not confident enough to expose publicly yet
*/
public static final int PRIMITIVE_THUD = 2;
/**
* A haptic effect that simulates spinning momentum.
+ *
+ * @hide Not confident enough to expose publicly yet
*/
public static final int PRIMITIVE_SPIN = 3;
/**
@@ -1003,7 +1007,8 @@
* This very short effect should produce a light crisp sensation intended
* to be used repetitively for dynamic feedback.
*/
- public static final int PRIMITIVE_LIGHT_TICK = 7;
+ // Internally this maps to the HAL constant CompositePrimitive::LIGHT_TICK
+ public static final int PRIMITIVE_TICK = 7;
private ArrayList<PrimitiveEffect> mEffects = new ArrayList<>();
@@ -1081,7 +1086,7 @@
*
*/
static int checkPrimitive(int primitiveId) {
- Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_LIGHT_TICK,
+ Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_TICK,
"primitiveId");
return primitiveId;
}
@@ -1108,8 +1113,8 @@
return "PRIMITIVE_SLOW_RISE";
case PRIMITIVE_QUICK_FALL:
return "PRIMITIVE_QUICK_FALL";
- case PRIMITIVE_LIGHT_TICK:
- return "PRIMITIVE_LIGHT_TICK";
+ case PRIMITIVE_TICK:
+ return "PRIMITIVE_TICK";
default:
return Integer.toString(id);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 34cec06..5f3f14f 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,6 +333,7 @@
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
+ * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
*
* @param zygoteArgs Additional arguments to supply to the Zygote process.
* @return An object that describes the result of the attempt to start the process.
@@ -354,6 +355,7 @@
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
// TODO (chriswailes): Is there a better place to check this value?
if (fetchUsapPoolEnabledPropWithMinInterval()) {
@@ -365,7 +367,7 @@
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, zygoteArgs);
+ pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -606,6 +608,7 @@
* @param disabledCompatChanges a list of disabled compat changes for the process being started.
* @param pkgDataInfoMap Map from related package names to private data directory volume UUID
* and inode number.
+ * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -628,6 +631,7 @@
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ boolean bindMountAppStorageDirs,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<>();
@@ -725,6 +729,10 @@
argsForZygote.add(sb.toString());
}
+ if (bindMountAppStorageDirs) {
+ argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
+ }
+
if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--disabled-compat-changes=");
@@ -1282,7 +1290,9 @@
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
true /* startChildZygote */, null /* packageName */,
ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
- null /* disabledCompatChanges */, null /* pkgDataInfoMap */, extraArgs);
+ null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
+ /* bindMountAppStorageDirs */ false, extraArgs);
+
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
}
diff --git a/core/java/android/os/incremental/IncrementalNewFileParams.aidl b/core/java/android/os/incremental/IncrementalNewFileParams.aidl
index 182732c..8faf158 100644
--- a/core/java/android/os/incremental/IncrementalNewFileParams.aidl
+++ b/core/java/android/os/incremental/IncrementalNewFileParams.aidl
@@ -16,8 +16,6 @@
package android.os.incremental;
-import android.os.incremental.IncrementalSignature;
-
/**
* All the parameters to create a new file on IncFS
* FileId is a 16 byte-long identifier.
@@ -27,5 +25,5 @@
long size;
byte[] fileId;
byte[] metadata;
- @nullable IncrementalSignature signature;
+ @nullable byte[] signature;
}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index bf31bc2..7092751 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -20,8 +20,6 @@
import android.annotation.Nullable;
import android.os.RemoteException;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -180,11 +178,12 @@
if (id == null && metadata == null) {
throw new IOException("File ID and metadata cannot both be null");
}
+ validateV4Signature(v4signatureBytes);
final IncrementalNewFileParams params = new IncrementalNewFileParams();
params.size = size;
params.metadata = (metadata == null ? new byte[0] : metadata);
params.fileId = idToBytes(id);
- params.signature = parseV4Signature(v4signatureBytes);
+ params.signature = v4signatureBytes;
int res = mService.makeFile(mId, path, params);
if (res != 0) {
throw new IOException("makeFile() failed with errno " + -res);
@@ -415,27 +414,23 @@
return new UUID(msb, lsb);
}
- private static final int INCFS_HASH_SHA256 = 1;
private static final int INCFS_MAX_HASH_SIZE = 32; // SHA256
private static final int INCFS_MAX_ADD_DATA_SIZE = 128;
/**
* Deserialize and validate v4 signature bytes.
*/
- private static IncrementalSignature parseV4Signature(@Nullable byte[] v4signatureBytes)
+ private static void validateV4Signature(@Nullable byte[] v4signatureBytes)
throws IOException {
if (v4signatureBytes == null || v4signatureBytes.length == 0) {
- return null;
+ return;
}
final V4Signature signature;
- try (DataInputStream input = new DataInputStream(
- new ByteArrayInputStream(v4signatureBytes))) {
- try {
- signature = V4Signature.readFrom(input);
- } catch (IOException e) {
- throw new IOException("Failed to read v4 signature:", e);
- }
+ try {
+ signature = V4Signature.readFrom(v4signatureBytes);
+ } catch (IOException e) {
+ throw new IOException("Failed to read v4 signature:", e);
}
if (!signature.isVersionSupported()) {
@@ -443,25 +438,27 @@
+ " is not supported");
}
- final byte[] rootHash = signature.verityRootHash;
- final byte[] additionalData = signature.v3Digest;
- final byte[] pkcs7Signature = signature.pkcs7SignatureBlock;
+ final V4Signature.HashingInfo hashingInfo = V4Signature.HashingInfo.fromByteArray(
+ signature.hashingInfo);
+ final V4Signature.SigningInfo signingInfo = V4Signature.SigningInfo.fromByteArray(
+ signature.signingInfo);
- if (rootHash.length != INCFS_MAX_HASH_SIZE) {
- throw new IOException("rootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
+ if (hashingInfo.hashAlgorithm != V4Signature.HASHING_ALGORITHM_SHA256) {
+ throw new IOException("Unsupported hashAlgorithm: " + hashingInfo.hashAlgorithm);
}
- if (additionalData.length > INCFS_MAX_ADD_DATA_SIZE) {
+ if (hashingInfo.log2BlockSize != V4Signature.LOG2_BLOCK_SIZE_4096_BYTES) {
+ throw new IOException("Unsupported log2BlockSize: " + hashingInfo.log2BlockSize);
+ }
+ if (hashingInfo.salt != null && hashingInfo.salt.length > 0) {
+ throw new IOException("Unsupported salt: " + hashingInfo.salt);
+ }
+ if (hashingInfo.rawRootHash.length != INCFS_MAX_HASH_SIZE) {
+ throw new IOException("rawRootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
+ }
+ if (signingInfo.additionalData.length > INCFS_MAX_ADD_DATA_SIZE) {
throw new IOException(
"additionalData has to be at most " + INCFS_MAX_ADD_DATA_SIZE + " bytes");
}
-
- IncrementalSignature result = new IncrementalSignature();
- result.hashAlgorithm = INCFS_HASH_SHA256;
- result.rootHash = rootHash;
- result.additionalData = additionalData;
- result.signature = pkcs7Signature;
-
- return result;
}
/**
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 6d334f5..71f931d 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -20,9 +20,12 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
+import java.io.EOFException;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* V4 signature fields.
@@ -31,30 +34,95 @@
*/
public class V4Signature {
public static final String EXT = ".idsig";
- public static final int SUPPORTED_VERSION = 1;
+ public static final int SUPPORTED_VERSION = 2;
- public final int version;
- public final byte[] verityRootHash;
- public final byte[] v3Digest;
- public final byte[] pkcs7SignatureBlock;
+ public static final int HASHING_ALGORITHM_SHA256 = 1;
+ public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12;
+
+ /**
+ * IncFS hashing data.
+ */
+ public static class HashingInfo {
+ public final int hashAlgorithm; // only 1 == SHA256 supported
+ public final byte log2BlockSize; // only 12 (block size 4096) supported now
+ public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
+ public final byte[] rawRootHash; // salted digest of the first Merkle tree page
+
+ HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
+ this.hashAlgorithm = hashAlgorithm;
+ this.log2BlockSize = log2BlockSize;
+ this.salt = salt;
+ this.rawRootHash = rawRootHash;
+ }
+
+ /**
+ * Constructs HashingInfo from byte array.
+ */
+ public static HashingInfo fromByteArray(byte[] bytes) throws IOException {
+ ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+ final int hashAlgorithm = buffer.getInt();
+ final byte log2BlockSize = buffer.get();
+ byte[] salt = readBytes(buffer);
+ byte[] rawRootHash = readBytes(buffer);
+ return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash);
+ }
+ }
+
+ /**
+ * V4 signature data.
+ */
+ public static class SigningInfo {
+ public final byte[] v3Digest; // used to match with the corresponding APK
+ public final byte[] certificate; // ASN.1 DER form
+ public final byte[] additionalData; // a free-form binary data blob
+ public final byte[] publicKey; // ASN.1 DER, must match the certificate
+ public final int signatureAlgorithmId; // see the APK v2 doc for the list
+ public final byte[] signature;
+
+ SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData,
+ byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
+ this.v3Digest = v3Digest;
+ this.certificate = certificate;
+ this.additionalData = additionalData;
+ this.publicKey = publicKey;
+ this.signatureAlgorithmId = signatureAlgorithmId;
+ this.signature = signature;
+ }
+
+ /**
+ * Constructs SigningInfo from byte array.
+ */
+ public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
+ ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+ byte[] v3Digest = readBytes(buffer);
+ byte[] certificate = readBytes(buffer);
+ byte[] additionalData = readBytes(buffer);
+ byte[] publicKey = readBytes(buffer);
+ int signatureAlgorithmId = buffer.getInt();
+ byte[] signature = readBytes(buffer);
+ return new SigningInfo(v3Digest, certificate, additionalData, publicKey,
+ signatureAlgorithmId, signature);
+ }
+ }
+
+ public final int version; // Always 2 for now.
+ public final byte[] hashingInfo;
+ public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later.
/**
* Construct a V4Signature from .idsig file.
*/
public static V4Signature readFrom(ParcelFileDescriptor pfd) throws IOException {
- final ParcelFileDescriptor dupedFd = pfd.dup();
- final ParcelFileDescriptor.AutoCloseInputStream fdInputStream =
- new ParcelFileDescriptor.AutoCloseInputStream(dupedFd);
- try (DataInputStream stream = new DataInputStream(fdInputStream)) {
+ try (InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd.dup())) {
return readFrom(stream);
}
}
/**
- * Construct a V4Signature from .idsig file.
+ * Construct a V4Signature from a byte array.
*/
public static V4Signature readFrom(byte[] bytes) throws IOException {
- try (DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes))) {
+ try (InputStream stream = new ByteArrayInputStream(bytes)) {
return readFrom(stream);
}
}
@@ -63,51 +131,131 @@
* Store the V4Signature to a byte-array.
*/
public byte[] toByteArray() {
- try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
- try (DataOutputStream steam = new DataOutputStream(byteArrayOutputStream)) {
- this.writeTo(steam);
- steam.flush();
- }
- return byteArrayOutputStream.toByteArray();
+ try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+ this.writeTo(stream);
+ return stream.toByteArray();
} catch (IOException e) {
return null;
}
}
- boolean isVersionSupported() {
+ /**
+ * Combines necessary data to a signed data blob.
+ * The blob can be validated against signingInfo.signature.
+ *
+ * @param fileSize - size of the signed file (APK)
+ */
+ public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
+ SigningInfo signingInfo) {
+ final int size =
+ 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
+ hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
+ signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize(
+ signingInfo.additionalData);
+ ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putInt(size);
+ buffer.putLong(fileSize);
+ buffer.putInt(hashingInfo.hashAlgorithm);
+ buffer.put(hashingInfo.log2BlockSize);
+ writeBytes(buffer, hashingInfo.salt);
+ writeBytes(buffer, hashingInfo.rawRootHash);
+ writeBytes(buffer, signingInfo.v3Digest);
+ writeBytes(buffer, signingInfo.certificate);
+ writeBytes(buffer, signingInfo.additionalData);
+ return buffer.array();
+ }
+
+ public boolean isVersionSupported() {
return this.version == SUPPORTED_VERSION;
}
- static V4Signature readFrom(DataInputStream stream) throws IOException {
- final int version = stream.readInt();
- byte[] verityRootHash = readBytes(stream);
- byte[] v3Digest = readBytes(stream);
- byte[] pkcs7SignatureBlock = readBytes(stream);
- return new V4Signature(version, verityRootHash, v3Digest, pkcs7SignatureBlock);
- }
-
- V4Signature(int version, byte[] verityRootHash, byte[] v3Digest, byte[] pkcs7SignatureBlock) {
+ private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) {
this.version = version;
- this.verityRootHash = verityRootHash;
- this.v3Digest = v3Digest;
- this.pkcs7SignatureBlock = pkcs7SignatureBlock;
+ this.hashingInfo = hashingInfo;
+ this.signingInfo = signingInfo;
}
- void writeTo(DataOutputStream stream) throws IOException {
- stream.writeInt(this.version);
- writeBytes(stream, this.verityRootHash);
- writeBytes(stream, this.v3Digest);
- writeBytes(stream, this.pkcs7SignatureBlock);
+ private static V4Signature readFrom(InputStream stream) throws IOException {
+ final int version = readIntLE(stream);
+ final byte[] hashingInfo = readBytes(stream);
+ final byte[] signingInfo = readBytes(stream);
+ return new V4Signature(version, hashingInfo, signingInfo);
}
- private static byte[] readBytes(DataInputStream stream) throws IOException {
- byte[] result = new byte[stream.readInt()];
- stream.read(result);
- return result;
+ private void writeTo(OutputStream stream) throws IOException {
+ writeIntLE(stream, this.version);
+ writeBytes(stream, this.hashingInfo);
+ writeBytes(stream, this.signingInfo);
}
- private static void writeBytes(DataOutputStream stream, byte[] bytes) throws IOException {
- stream.writeInt(bytes.length);
+ // Utility methods.
+ private static int bytesSize(byte[] bytes) {
+ return 4/*length*/ + (bytes == null ? 0 : bytes.length);
+ }
+
+ private static void readFully(InputStream stream, byte[] buffer) throws IOException {
+ int len = buffer.length;
+ int n = 0;
+ while (n < len) {
+ int count = stream.read(buffer, n, len - n);
+ if (count < 0) {
+ throw new EOFException();
+ }
+ n += count;
+ }
+ }
+
+ private static int readIntLE(InputStream stream) throws IOException {
+ final byte[] buffer = new byte[4];
+ readFully(stream, buffer);
+ return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
+ }
+
+ private static void writeIntLE(OutputStream stream, int v) throws IOException {
+ final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt(
+ v).array();
+ stream.write(buffer);
+ }
+
+ private static byte[] readBytes(InputStream stream) throws IOException {
+ try {
+ final int size = readIntLE(stream);
+ final byte[] bytes = new byte[size];
+ readFully(stream, bytes);
+ return bytes;
+ } catch (EOFException ignored) {
+ return null;
+ }
+ }
+
+ private static byte[] readBytes(ByteBuffer buffer) throws IOException {
+ if (buffer.remaining() < 4) {
+ throw new EOFException();
+ }
+ final int size = buffer.getInt();
+ if (buffer.remaining() < size) {
+ throw new EOFException();
+ }
+ final byte[] bytes = new byte[size];
+ buffer.get(bytes);
+ return bytes;
+ }
+
+ private static void writeBytes(OutputStream stream, byte[] bytes) throws IOException {
+ if (bytes == null) {
+ writeIntLE(stream, 0);
+ return;
+ }
+ writeIntLE(stream, bytes.length);
stream.write(bytes);
}
+
+ private static void writeBytes(ByteBuffer buffer, byte[] bytes) {
+ if (bytes == null) {
+ buffer.putInt(0);
+ return;
+ }
+ buffer.putInt(bytes.length);
+ buffer.put(bytes);
+ }
}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index a2def7f..e05991b 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -100,10 +100,11 @@
}
/**
- * Check if fuse is running in target user, if it's running then setup its obb directories.
- * TODO: System server should store a list of active pids that obb is not mounted and use it.
+ * Create storage directories if it does not exist.
+ * Return true if the directories were setup correctly, otherwise false.
*/
- public abstract void prepareObbDirs(int userId, Set<String> packageList, String processName);
+ public abstract boolean prepareStorageDirs(int userId, Set<String> packageList,
+ String processName);
/**
* Add a listener to listen to reset event in StorageManagerService.
@@ -143,4 +144,10 @@
* @param uid the uid of the package
*/
public abstract void prepareAppDataAfterInstall(@NonNull String packageName, int uid);
+
+
+ /**
+ * Return true if uid is external storage service.
+ */
+ public abstract boolean isExternalStorageService(int uid);
}
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 0483514..f011395 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -42,6 +42,6 @@
void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName,
String permission, int grantState, in AndroidFuture callback);
void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
- void updateUserSensitive(in AndroidFuture callback);
void notifyOneTimePermissionSessionTimeout(String packageName);
+ void updateUserSensitiveForApp(int uid, in AndroidFuture callback);
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 2615c98..09df72c 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -106,4 +106,12 @@
int importanceToResetTimer, int importanceToKeepSessionAlive);
void stopOneTimePermissionSession(String packageName, int userId);
+
+ List<String> getAutoRevokeExemptionRequestedPackages(int userId);
+
+ List<String> getAutoRevokeExemptionGrantedPackages(int userId);
+
+ boolean setAutoRevokeWhitelisted(String packageName, boolean whitelisted, int userId);
+
+ boolean isAutoRevokeWhitelisted(String packageName, int userId);
}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 2a1857f..f08e3d25 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -46,6 +46,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Process;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
@@ -626,14 +627,26 @@
}
/**
- * @see PermissionControllerService#onUpdateUserSensitive()
+ * @see PermissionControllerManager#updateUserSensitiveForApp
* @hide
*/
public void updateUserSensitive() {
+ updateUserSensitiveForApp(Process.INVALID_UID);
+ }
+
+ /**
+ * @see PermissionControllerService#onUpdateUserSensitiveForApp
+ * @hide
+ */
+ public void updateUserSensitiveForApp(int uid) {
mRemoteService.postAsync(service -> {
AndroidFuture<Void> future = new AndroidFuture<>();
- service.updateUserSensitive(future);
+ service.updateUserSensitiveForApp(uid, future);
return future;
+ }).whenComplete((res, err) -> {
+ if (err != null) {
+ Log.e(TAG, "Error updating user_sensitive flags for uid " + uid, err);
+ }
});
}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 263b2c7..4a42230 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -218,11 +218,14 @@
* Called by system to update the
* {@link PackageManager}{@code .FLAG_PERMISSION_USER_SENSITIVE_WHEN_*} flags for permissions.
* <p>
- * This is typically when creating a new user or upgrading either system or
- * permission controller package.
+ *
+ * If uid is -1, updates the permission flags for all packages.
+ *
+ * Typically called by the system when a new app is installed or updated or when creating a
+ * new user or upgrading either system or permission controller package.
*/
@BinderThread
- public void onUpdateUserSensitivePermissionFlags() {
+ public void onUpdateUserSensitivePermissionFlags(int uid, @NonNull Runnable callback) {
throw new AbstractMethodError("Must be overridden in implementing class");
}
@@ -459,11 +462,14 @@
}
@Override
- public void updateUserSensitive(AndroidFuture callback) {
+ public void updateUserSensitiveForApp(int uid, @NonNull AndroidFuture callback) {
Preconditions.checkNotNull(callback, "callback cannot be null");
- onUpdateUserSensitivePermissionFlags();
- callback.complete(null);
+ try {
+ onUpdateUserSensitivePermissionFlags(uid, () -> callback.complete(null));
+ } catch (Exception e) {
+ callback.completeExceptionally(e);
+ }
}
@Override
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 0bd211d..8308bb3 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -40,11 +40,13 @@
import android.util.Slog;
import com.android.internal.annotations.Immutable;
+import com.android.internal.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -299,6 +301,46 @@
}
}
+ /**
+ * Gets the list of packages that have permissions that specified
+ * {@code requestDontAutoRevokePermissions=true} in their
+ * {@code application} manifest declaration.
+ *
+ * @return the list of packages for current user
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
+ public Set<String> getAutoRevokeExemptionRequestedPackages() {
+ try {
+ return CollectionUtils.toSet(mPermissionManager.getAutoRevokeExemptionRequestedPackages(
+ mContext.getUser().getIdentifier()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the list of packages that have permissions that specified
+ * {@code allowDontAutoRevokePermissions=true} in their
+ * {@code application} manifest declaration.
+ *
+ * @return the list of packages for current user
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
+ public Set<String> getAutoRevokeExemptionGrantedPackages() {
+ try {
+ return CollectionUtils.toSet(mPermissionManager.getAutoRevokeExemptionGrantedPackages(
+ mContext.getUser().getIdentifier()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
List<SplitPermissionInfoParcelable> parcelableList) {
final int size = parcelableList.size();
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
new file mode 100644
index 0000000..e116dc6
--- /dev/null
+++ b/core/java/android/permission/Permissions.md
@@ -0,0 +1,906 @@
+<!--
+ 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
+ -->
+
+# Android permissions for system developers
+
+This document targets system developers. App developers should refer to the [public
+ documentation](https://developer.android.com/guide/topics/permissions/overview).
+
+## Definitions
+
+Each app (often called package) has a unique name called package name. Each package has a manifest
+file describing properties of the package. The android system server is in a special package named
+"android".
+
+When a package gets installed the package is (usually) assigned a unique identifier called [uid
+](../os/Users.md#int-uid).
+This is the same as a uid in Linux, but this often leads to confusion. It is easiest to see a uid
+as a unique identifier for a package.
+
+Usually an app is running in a container called a process. Each process has a unique id called
+pid, but unlike the uid the pid changes each time the process is restarted and app that are not
+currently running don't have a pid. The process container makes sure that other apps cannot
+negatively interact with an app. Processes can only interact via controlled interactions called
+remote procedure calls (RPCs). Android’s RPC mechanism is called _Binder_.
+
+As no app code can be trusted the permission need to be checked on the receiving side of the
+Binder call.
+
+For more details please take a look at [Android's security model](../os/Users.md#security-model).
+
+## Permissions for regular apps
+
+### Install time permissions
+
+The purpose of install time permissions is to control access to APIs where it does not makes sense
+to involve the user. This can be either because the API is not sensitive, or because additional
+checks exist.
+
+Another benefit of install time permissions is that is becomes very easy to monitor which apps can
+access certain APIs. E.g. by checking which apps have the `android.permission.INTERNET` permission
+you can list all apps that are allowed to use APIs that can send data to the internet.
+
+#### Defining a permission
+
+Any package can define a permission. For that it simply adds an entry in the manifest file
+`<permission android:name="com.example.myapp.myfirstpermission" />`
+
+Any package can do this, including the system package. When talking about [permissions for system
+ apps](#permissions-for-system-apps) we will see that it is important which package defines a
+permission.
+
+It is common good practice to prefix the permission name with the package name to avoid collisions.
+
+#### Requesting a permission
+
+Any app can request any permission via adding an entry in the manifest file like
+`<uses-permission android:name="com.example.myapp.myfirstpermission" />`
+
+A requested permission does not necessarily mean that the permission is granted. When and how a
+permission is granted depends on the protection level of the permission. If no protection level is
+set, the permission will always be granted. Such "normal" permissions can still be useful as it
+will be easy to find apps using a certain functionality on app stores and by checking `dumpsys
+package`.
+
+#### Checking a permission
+
+`Context.checkPermission(permission, pid, uid)` returns if the pid/uid has the permission. By far
+the most common case is to check the permission on the receiving end of a binder call. In this case
+the pid can be read as `Binder.callingPid()` and the uid as `Binder.callingUid()`. The uid is a
+mandatory argument as permissions are maintained per uid. The pid can be set to -1
+if not pid is available. The context class contains handy wrappers for `checkPermission`, such as
+`enforeCallingPermission` which calls checkPermission with `Binder.callingPid`/`Binder.callingUid`
+and throws a SecurityException when the permission is not granted.
+
+#### Verifying an app has an install time permission
+
+In `dumpsys package my.package.name` there are two sections. In requested permissions all
+permissions of the `uses-permission` tags are listed. In install permission the permissions with
+their grant state are listed. If an install time permission is not listed here, it is not granted.
+
+```
+Packages:
+ Package [com.android.packageinstaller] (2eb7062):
+ userId=10071
+ [...]
+ requested permissions:
+ android.permission.MANAGE_USERS
+ android.permission.INSTALL_PACKAGES
+ android.permission.DELETE_PACKAGES
+ android.permission.READ_INSTALL_SESSIONS
+ android.permission.RECEIVE_BOOT_COMPLETED
+ android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS
+ android.permission.USE_RESERVED_DISK
+ android.permission.UPDATE_APP_OPS_STATS
+ android.permission.MANAGE_APP_OPS_MODES
+ android.permission.INTERACT_ACROSS_USERS_FULL
+ android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME
+ android.permission.PACKAGE_USAGE_STATS
+ install permissions:
+ android.permission.USE_RESERVED_DISK: granted=true
+ android.permission.INSTALL_PACKAGES: granted=true
+ android.permission.RECEIVE_BOOT_COMPLETED: granted=true
+ android.permission.INTERACT_ACROSS_USERS_FULL: granted=true
+ android.permission.PACKAGE_USAGE_STATS: granted=true
+ android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME: granted=true
+ android.permission.READ_INSTALL_SESSIONS: granted=true
+ android.permission.MANAGE_USERS: granted=true
+ android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS: granted=true
+ android.permission.MANAGE_APP_OPS_MODES: granted=true
+ android.permission.UPDATE_APP_OPS_STATS: granted=true
+ android.permission.DELETE_PACKAGES: granted=true
+```
+
+#### End-to-end: Protecting an RPC call via a permission
+
+##### Service Manifest
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.myservice">
+ <!-- Define a permission -->
+ <permission android:name="com.android.example.myservice.MY_PERMISSION" />
+ <application>
+ <service android:name=".MyService" android:exported="true" />
+ </application>
+</manifest>
+```
+
+##### Service code
+
+```kotlin
+class MyService : Service() {
+ override fun onBind(intent: Intent?): IBinder? {
+ return object : IMyService.Stub() {
+ override fun doSomething() {
+ // Verify that calling UID has the permission
+ enforceCallingPermission(
+ "com.android.example.myservice.MY_PERMISSION",
+ "Need to hold permission"
+ )
+ // do something
+ }
+ }.asBinder()
+ }
+}
+```
+
+##### Caller Manifest
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.myapp">
+ <!-- request a permission -->
+ <uses-permission android:name="com.android.example.myservice.MY_PERMISSION" />
+ <application />
+</manifest>
+```
+
+### Runtime permissions
+
+Runtime permission must be granted by the user during runtime. This is needed if the API protects
+data or functionality that is sensitive for the user. E.g. the users current location is protected
+by a runtime permission.
+
+Users want a system that is secure and privacy focused by default. User can also often not make a
+good choice when asked at the wrong time without enough context. Hence in general runtime
+permissions should be avoided and the API should be built in a way where no private data needs to be
+leaked.
+
+#### Defining a runtime permission
+
+Runtime permissions are defined in the same way as install time permissions. To tag them as runtime
+permissions the `protectionLevel` needs to be set to dangerous. Dangerous is a synonym for
+runtime permissions in the Android platform.
+
+```xml
+<uses-permission android:name="com.example.myapp.myfirstruntimepermission"
+ android:protectionLevel="dangerous" />
+```
+
+#### Requesting a runtime permission
+
+Similar to install time permissions any app can request a runtime permission by adding the
+`<uses-permission android:name="com.example.myapp.myfirstruntimepermission" />`
+to the manifest.
+
+By default runtime permissions are not granted. The app needs to call `Activity.requestPermissions`
+during runtime to ask the user for the permission. The user might then grant or deny and once the
+decision is made the activity is called by via `Activity.onPermissionGranted`.
+
+During development and testing a runtime permission can be granted via the `pm` shell command or by
+using the `UiAutomator.grantRuntimePermission` API call. Please note that this does _not_ grant the
+[app-op](#runtime-permissions-and-app-ops) synchronously. Unless the app needs to test the actual
+permission grant flow it is recommended to grant the runtime permissions during install using
+`adb install -g /my/package.apk`.
+
+#### Checking a runtime permission
+
+For runtime permissions defined by a 3rd party apps it is fine to check a runtime
+permission like an install time permission. For system defined permissions you need to check all
+runtime permissions by using the `PermissionChecker` utility. It is good practice to use the tool
+anywhere possible.
+
+The permission checker might return `PERMISSION_DENIED_APP_OP` which should lead to a silent
+failure. This can only happen for system defined runtime permissions.
+
+##### Runtime permissions and app-ops
+
+> See [App-ops](../app/AppOps.md).
+
+The PermissionChecker code fundamentally looks like this:
+
+```kotlin
+class PermissionChecker {
+ fun checkCallingPermission(context: Context, permission: String) {
+ if (isRuntimePermission(permission)) {
+ if (context.checkPermission(uid, permission) == DENIED) {
+ return PERMISSION_DENIED
+ }
+
+ val appOpOfPermission = AppOpsManager.permissionToOp(permission)
+ if (appOpOfPermission == null) {
+ // not platform defined
+ return PERMISSION_GRANTED
+ }
+
+ val appOpMode = appOpsManager.noteOp(appOpOfPermission)
+ if (appOpMode == AppOpsManager.MODE_ALLOWED) {
+ return PERMISSION_GRANTED
+ } else {
+ return PERMISSION_DENIED_APP_OP
+ }
+ } else {
+ return PERMISSION_DENIED
+ }
+ }
+}
+```
+
+For each platform defined runtime permission there is a matching app-op. When calling
+`AppOpsManager.noteOp` this returns either `MODE_ALLOWED` or `MODE_IGNORED`.
+
+This value is then used to decide between `PERMISSION_DENIED_APP_OP` and `PERMISSION_GRANTED`.
+
+The primary purpose of the special `PERMISSION_DENIED_APP_OP` state was to support apps targeting an
+SDK lower than 23. These apps do not understand the concept of denied runtime permissions. Hence
+they would crash when getting a `SecurityException`. To protect the users' privacy while still not
+crashing the app the special `PERMISSION_DENIED_APP_OP` mandates that the API should somehow
+silently fail.
+
+A secondary use case of the `AppOpsManager.noteOp` calls is to
+[track](../app/AppOps.md#Appops-for-tracking) which apps perform what runtime protected actions.
+
+#### Verifying an app has a runtime time permission
+
+In `dumpsys package my.package.name` the runtime permissions are listed per uid. I.e. different
+users might have different runtime permission grants and shared uids share a grant-set. If a runtime
+permission is listed as requested but not in the runtime permission section it is in it’s initial
+state, i.e. not granted.
+
+```
+Packages:
+ Package [com.google.android.GoogleCamera] (ccb6af):
+ userId=10181
+ [...]
+ requested permissions:
+ android.permission.ACCESS_COARSE_LOCATION
+ android.permission.ACCESS_FINE_LOCATION
+ android.permission.ACCESS_NETWORK_STATE
+ android.permission.ACCESS_NOTIFICATION_POLICY
+ android.permission.ACCESS_WIFI_STATE
+ android.permission.BIND_WALLPAPER
+ android.permission.CAMERA
+ android.permission.CHANGE_WIFI_STATE
+ android.permission.INTERNET
+ android.permission.GET_PACKAGE_SIZE
+ android.permission.NFC
+ android.permission.READ_SYNC_SETTINGS
+ android.permission.RECEIVE_BOOT_COMPLETED
+ android.permission.RECORD_AUDIO
+ android.permission.SET_WALLPAPER
+ android.permission.USE_CREDENTIALS
+ android.permission.VIBRATE
+ android.permission.WAKE_LOCK
+ android.permission.WRITE_EXTERNAL_STORAGE [ ... ]
+ android.permission.WRITE_SETTINGS
+ android.permission.WRITE_SYNC_SETTINGS
+ com.google.android.elmyra.permission.CONFIGURE_ASSIST_GESTURE
+ com.google.android.providers.gsf.permission.READ_GSERVICES
+ android.permission.FOREGROUND_SERVICE
+ com.google.android.googlequicksearchbox.permission.LENSVIEW_BROADCAST
+ android.permission.READ_EXTERNAL_STORAGE [ ... ]
+ [...]
+ User 0: [ ... ]
+ overlay paths:
+ runtime permissions:
+ android.permission.ACCESS_FINE_LOCATION: granted=false [ ... ]
+ android.permission.READ_EXTERNAL_STORAGE: granted=true [ ... ]
+ android.permission.ACCESS_COARSE_LOCATION: granted=false [ ... ]
+ android.permission.CAMERA: granted=true [ ... ]
+ android.permission.WRITE_EXTERNAL_STORAGE: granted=true [ ... ]
+ android.permission.RECORD_AUDIO: granted=true[ ... ]
+```
+
+#### End-to-end: Protecting an RPC call via a runtime permission
+
+##### Service Manifest
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.myservice">
+ <!-- Define a runtime permission -->
+ <permission android:name="com.android.example.myservice.MY_RUNTIME_PERMISSION"
+ android:protectionLevel="dangerous" />
+ <application>
+ <service android:name=".MyService" android:exported="true" />
+ </application>
+</manifest>
+```
+
+##### Service code
+
+```kotlin
+class MyService : Service() {
+ override fun onBind(intent: Intent?): IBinder? {
+ return object : IMyService.Stub() {
+ override fun doSomething(callingPackage: String?, callingFeatureId: String?) {
+ Objects.requireNonNull(callingPackageName)
+
+ // Verify that calling UID has the permission
+ when (run {
+ PermissionChecker.checkCallingPermission(
+ this@MyService,
+ "com.android.example.myservice.MY_RUNTIME_PERMISSION",
+ callingPackageName,
+ callingFeatureId,
+ "Did something"
+ )
+ }) {
+ PERMISSION_GRANTED -> /* do something */
+ PERMISSION_DENIED_APP_OP -> /* silent failure, do nothing */
+ else -> throw SecurityException(
+ "Cannot do something as caller is missing "
+ + "com.android.example.myservice.MY_RUNTIME_PERMISSION"
+ )
+ }
+ }
+ }.asBinder()
+ }
+}
+```
+
+##### Caller Manifest
+
+```xml
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.myapp">
+ <!-- request a permission -->
+ <uses-permission android:name="com.android.example.myservice.MY_RUNTIME_PERMISSION" />
+ <application />
+</manifest>
+```
+
+##### Caller code
+
+```kotlin
+class MyActivity : Activity {
+ fun callDoSomething() {
+ if (checkSelfPermission("com.android.example.myservice.MY_RUNTIME_PERMISSION") == PERMISSION_DENIED) {
+ // Interrupt operation and request permission
+ requestPermissions(arrayOf("com.android.example.myservice.MY_RUNTIME_PERMISSION"), 23)
+ } else {
+ myService.doSomething(this@MyActivity.opPackageName, this@MyActivity.featureId)
+ }
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<String>,
+ grantResults: IntArray
+ ) {
+ if (requestCode == 23 && grantResults[0] == PERMISSION_GRANTED) {
+ // Finish operation
+ callDoSomething()
+ }
+ }
+}
+```
+
+#### Restricted permissions
+
+Some runtime permissions are restricted. They are annotated in the platforms `AndroidManifest.xml`
+has `hardRestricted` or `softRestricted`.
+
+Restricted permissions behave uncommon when not whitelisted. When whitelisted the permissions
+behave normally. What uncommon means depends on the whether they are hard or soft restricted.
+
+They can either be whitelisted during upgrade P->Q, but the system or need to be whitelisted by the
+installer via `PackageInstaller.SessionParams.setWhitelistedRestrictedPermissions`. If this method
+is not used all permissions will be whitelisted.
+
+Afterwards the app that originally installed the app can change the whitelisting state via
+`PackageManager.addWhitelistedRestrictedPermission` and
+`PackageManager.removeWhitelistedRestrictedPermission`.
+
+The system tracks the source of the whitelisting by having three different flags
+ `RESTRICTION_SYSTEM_EXEMPT`, `RESTRICTION_UPGRADE_EXEMPT`, and `RESTRICTION_INSTALLER_EXEMPT`,
+
+The flags can be checked in `dumpsys package my.package.name`
+
+```
+User 0:
+ [...]
+ runtime permissions:
+ android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ RESTRICTION_UPGRADE_EXEMPT ]
+ android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ RESTRICTION_SYSTEM_EXEMPT|RESTRICTION_UPGRADE_EXEMPT ]
+```
+
+##### Hard restricted
+
+Hard restricted permissions need to be whitelisted to be grant-able.
+
+##### Soft restricted
+
+The behavior of non-whitelisted soft restricted permissions is not uniform. The behavior is
+defined in the `SoftRestrictedPermissionPolicy`.
+
+#### System fixed permission
+
+Some runtime permissions are required for normal operation of the device. In this case the system
+can grant the permission as `SYSTEM_FIXED`. In this case the permission can be seen in the
+[permission management settings](#settings) but cannot be revoked by the user.
+
+The flag can be checked in `dumpsys package my.package.name`
+```
+User 0:
+ [...]
+ runtime permissions:
+ android.permission.READ_EXTERNAL_STORAGE: granted=true, flags=[ SYSTEM_FIXED|GRANTED_BY_DEFAULT ]
+```
+
+#### Background access
+
+Whether the app is currently visible to the user is reflected in the `ActivityManager`'s proc state.
+There is a lot of granularity to this, but runtime permissions are using the [app-ops services'
+](../app/AppOps.md) definition of foreground and background.
+
+Most runtime permissions are not affected by foreground/background-ness. Microphone and Camera are
+foreground-only while Location is usually foreground-only, but background access can be added by
+granting the `ACCESS_BACKGROUND_LOCATION` modifier runtime permission.
+
+##### Microphone and Camera
+
+Currently these only allow access while in the app is in foreground. There is a manual whitelist
+for e.g. the voice interaction service.
+
+This is currently (Mar 2020) reworked and will behave like [location](#location) soon.
+
+##### Location
+
+As described [above](#runtime-permissions-and-app-ops) the app-op mode for granted permissions is
+`MODE_ALLOWED` to allow access or `MODE_IGNORED` to suppress access.
+
+The important case is the case where the permission is granted and the app-op is `MODE_IGNORED`. In
+the case of location this state causes the `LocationManagerService` to stop delivering locations to
+the app. This is not a breaking behavior as the same scenarios happens if e.g. no satellites
+could be found.
+
+This behavior is used to implement the foregound/background behavior for location. If the app is
+in the foreground the app-op mode is `MODE_ALLOWED` and works normally. If the app goes into
+background the app-op mode changes to `MODE_IGNORED`. This means that locations are delivered while
+the app is in foreground and while the app is background, the app won't get any locations.
+
+The automatic switching between `MODE_ALLOWED` and `MODE_IGNORED` is done inside of
+ [`AppOpsManager`](../app/AppOps.md#foreground).
+
+Background access can be enabled by also granting the `ACCESS_BACKGROUND_LOCATION` to the app. In
+this case the app-op mode will always be `MODE_ALLOWED`.
+
+#### UI
+
+##### Granting
+
+An app following the best practices does not ask for any runtime permissions until absolutely
+needed. Once needed the request should be made in context. I.e. the user should understand from the
+current state of the app and the user's action why the request is made. E.g. if the user presses
+a "show me the next ATM"-button the user is most likely expecting a request for the location
+permission.
+
+This is central premise to the runtime permission UI. It is the app's responsibility to avoid
+showing permission requests dialogs to the user which might get denied. These dialogs are not
+meant to be user-choices, they are meant to be user-confirmations.
+
+Hence any denied permission dialog is probably due to the app asking for permissions the user
+does not expect. If too many permission requests get denied the app is apparently trying to get
+more than the user wants to give to the app. In this case the permission gets permanently denied
+and all future requests will be denied automatically without showing a UI.
+
+`Context.requestPermission` calls for more than one permission are allowed and might result in
+multiple dialogs in sequence. This might make sense for e.g. getting microphone and camera
+permission when starting a video call.
+
+Each time the the user makes a choice (either to grant or the deny) a permission request the
+permission is marked as `USER_SET`. If a permission gets permanently denied the permission is marked
+as `USER_FIXED`.
+
+This can be found in `dumpsys package my.package.name`
+```
+User 0:
+ [...]
+ runtime permissions:
+ android.permission.READ_EXTERNAL_STORAGE: granted=false, flags=[ USER_SET|USER_FIXED ]
+ android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ USER_SET ]
+```
+
+##### Settings
+
+By far most interactions with the permission system are via the [permission grant flow](#granting).
+The main purpose of the permission settings is to show the user the previous choices and allow
+the user to revisit previous choices. In reality few users do that.
+
+##### Grouping
+
+There are too many runtime permissions for the user to individually manage. Hence the UI bundles the
+permissions into groups. **Apps should never assume the grouping**. The grouping might change
+with SDK updates, but also at any other time. Certain form factors or locales might use other
+permission models and sometimes some of the permissions of a group cannot be granted for various
+reasons. The grouping is defined inside the permission controller app.
+
+If two permissions belong to a group and the first permission is already granted the second one
+will be granted on request of the app without user interaction. For that reason a permission
+group with at least one individual permission granted will show up as granted in the UI.
+
+##### Alternate permission management
+
+It is not allowed to build alternate permission management UIs. While restricting innovation is not
+a good choice this is a required one to enforce a consistent, predictable, but flexible permission
+model for users and app developers.
+
+Further some data needed for permission management (e.g. the grouping) is not available outside
+the permission controller app.
+
+Hence all permission management UI needs to be integrated with AOSP.
+
+#### Pre granting
+
+Runtime permissions protect user private data. It is a violation of user trust to give the data
+to an app without explicit user consent (i.e. the user [granting](#granting) the permission
+). Still the user expects certain functionality (e.g. receiving a phone call) to work out of the
+box.
+
+Hence the `DefaultPermissionGrantPolicy` and roles allow to grant permission without the user
+. The default permission grant policy grants permissions for three categories of apps
+- Apps running in well defined [uids](../os/Users.md#int-uid) as they are considered as part of
+ the platform
+- Apps that are in certain predefined categories, e.g. the browser and the SMS app. This is
+ meant for the most basic phone functionality, not for all pre-installed apps.
+- Apps that are explicitly mentioned as a pre-grant-exceptions. This is meant to be used for setup
+ and other highly critical use cases, not to improve the user experience. The exceptions are listed
+ in xml files in `etc/` and follow the following syntax
+```xml
+<exceptions>
+ <exception package="my.package.name">
+ <permission name="android.permission.ACCESS_FINE_LOCATION" fixed="false"/>
+ </exception>
+</exceptions>
+```
+
+Pre-granted runtime permissions can still be revoked by the user in [settings](#settings) unless
+they are granted as `SYSTEM_FIXED`.
+
+Whether a permission was granted by the default can be checked in the permission flags of
+`dumpsys package my.package.name`
+
+```
+User 0:
+ [...]
+ runtime permissions:
+ android.permission.ACCESS_FINE_LOCATION: granted=true, flags=[ GRANTED_BY_DEFAULT ]
+```
+
+### Permission restricted components
+
+As [publicly documented](https://developer.android.com/guide/topics/permissions/overview#permission_enforcement)
+it is possible to restrict starting an activity/binding to a service by using permission.
+
+It is a common pattern to
+
+- define a permission in the platform as `signature`
+- protect a service in an app by this permission using the `android:permission` attribute of the
+ `<service>` tag
+
+Then it is guaranteed that only the system can bind to such service. This is used for services
+that provide extensions to platform functionality, such as auto-fill services, print services, and
+accessibility services.
+
+This does not work for app-op or runtime permissions as the way to check these permissions is
+more complex than install time permissions.
+
+#### End-to-end A service only the system can bind to
+
+Make sure to set the `android:permission` flag for this service. As developers can forget this it is
+a good habit to check this before binding to the service. This makes sure that the services are
+implemented correctly and no random app can bind to the service.
+
+The result is that the permission is granted only to the system. It is not granted to the service's
+package, but this has no negative side-effects.
+
+##### Permission definition
+
+frameworks/base/core/res/AndroidManifest.xml:
+
+```xml
+<manifest>
+[...]
+ <permission android:name="android.permission.BIND_MY_SERVICE"
+ android:label="@string/permlab_bindMyService"
+ android:description="@string/permdesc_bindMyService"
+ android:protectionLevel="signature" />
+[...]
+</manifest>
+```
+
+##### Service definition
+
+Manifest of the service providing the functionality:
+
+```xml
+<manifest>
+ <service android:name="com.my.ServiceImpl"
+ android:permission="android.permission.BIND_MY_SERVICE">
+ <!-- add an intent filter so that the system can find this package -->
+ <intent-filter>
+ <action android:name="android.my.Service" />
+ </intent-filter>
+ </service>
+</manifest>
+```
+
+##### System server code binding to service
+
+```kotlin
+val serviceConnections = mutableListOf<ServiceConnection>()
+
+val potentialServices = context.packageManager.queryIntentServicesAsUser(
+ Intent("android.my.Service"), GET_SERVICES or GET_META_DATA, userId)
+
+for (val ri in potentialServices) {
+ val serviceComponent = ComponentName(ri.serviceInfo.packageName, ri.serviceInfo.name)
+
+ if (android.Manifest.permission.BIND_MY_SERVICE != ri.serviceInfo.permission) {
+ Slog.w(TAG, "$serviceComponent is not protected by " +
+ "${android.Manifest.permission.BIND_MY_SERVICE}")
+ continue
+ }
+
+ val newConnection = object : ServiceConnection {
+ ...
+ }
+
+ val wasBound = context.bindServiceAsUser(Intent().setComponent(serviceComponent),
+ serviceConnection, BIND_AUTO_CREATE, UserHandle.of(userId))
+ if (wasBound) {
+ serviceConnections.add(newConnection)
+ }
+}
+```
+
+## Permissions for system apps
+
+System apps need to integrate deeper with the system than regular apps. Hence they need to be
+able to call APIs not available to other apps. This is implemented by granting permissions to
+these system apps and then enforcing the permissions in the API similar to other [install time
+permissions](#checking-a-permission).
+
+System apps are not different from regular apps, but the protection levels (e.g.
+[privileged](#privileged-permissions), [preinstalled](#preinstalled-permissions)) mentioned in this
+section are more commonly used by system apps.
+
+### Multiple permission levels
+
+It is possible to assign multiple protection levels to a permission. Very common combinations are
+for example adding `signature` to all permissions to make sure the platform signed apps can be
+granted the permission, e.g. `privileged|signature`.
+
+The permission will be granted if the app qualifies for _any_ of the permission levels.
+
+### App-op permissions
+
+> See [App-ops](../app/AppOps.md).
+
+App-op permissions are user-switchable permissions that are not runtime permissions. This should
+be used for permissions that are really only meant to be ever granted to a very small amount of
+apps. Traditionally granting these permissions is intentionally very heavy weight so that the
+user really needs to understand the use case. For example one use case is the
+`INTERACT_ACROSS_PROFILES` permission that allows apps of different
+[user profiles](../os/Users.md#user-profile) to interact. Of course this is breaking a very basic
+security container and hence should only every be granted with a lot of care.
+
+**Warning:** Most app-op permissions follow this logic, but most of them also have exceptions
+and special behavior. Hence this section is a guideline, not a rule.
+
+#### Defining an app-op permission
+
+Only the platform can reasonably define an app-op permission. The permission is defined in the
+platforms manifest using the `appop` protection level
+
+```xml
+<manifest package="android">
+ <permission android:name="android.permission.MY_APPOP_PERMISSION"
+ android:protectionLevel="appop|signature" />
+</manifest>
+```
+
+Almost always the protection level is app-op | something else, like
+[signature](#signature-permissions) (in the case above) or [privileged](#privileged-permissions).
+
+#### Checking a app-op permission
+
+The `PermissionChecker` utility can check app-op permissions with the [same syntax as runtime
+permissions](#checking-a-runtime-permission).
+
+The permission checker internally follows this flow
+
+```kotlin
+class PermissionChecker {
+ fun checkCallingPermission(context: Context, permission: String) {
+ if (isAppOpPermission(permission)) {
+ val appopOfPermission = AppOpsManager.permissionToOp(permission)
+ if (appopOfPermission == null) {
+ // not platform defined
+ return PERMISSION_DENIED
+ }
+
+ val appopMode = appOpsManager.noteOp(appopOfPermission)
+ when (appopMode) {
+ AppOpsManager.MODE_ALLOWED -> return PERMISSION_GRANTED
+ AppOpsManager.MODE_IGNORED -> return PERMISSION_DENIED
+ AppOpsManager.MODE_DEFAULT -> {
+ if (context.checkPermission(uid, permission) == GRANTED) {
+ return PERMISSION_GRANTED
+ } else {
+ return PERMISSION_DENIED
+ }
+ }
+ }
+ } else {
+ return PERMISSION_DENIED
+ }
+ }
+}
+```
+
+#### Granting a app-op permission
+
+The permission's grant state is only considered if the app-op's mode is `MODE_DEFAULT`. This
+allows to have default grants while still being overridden by the app-op.
+
+The permission is then granted by setting the app-op mode. This is usually done via dedicated APIs
+for each use cases. Similarly whether and how an app can request the permission is different for
+each app-op permission.
+
+When implementing a new app-op permission, make sure to set the app-op mode using `AppOpsManager
+.setUidMode` to make sure the permission is granted on the uid as this is the security domain.
+
+During development app-ops can be grated to app via the `appops set` shell command. E.g.
+
+```
+adb shell appops set 10187 INTERACT_ACROSS_PROFILES allow
+```
+
+sets the `INTERACT_ACROSS_PROFILES` app-op for uid 10187 to allow thereby granting apps in this
+uid the ability to interact across profiles.
+
+##### UI
+
+Most UIs for app-op permissions are in the "Special app access" section of the settings app.
+
+In most cases the permission should only be granted with the user's explicit agreement, usually by
+allowing the app to directly open the "Special app access" page for this permission and app.
+
+To repeat: this is a guideline for app-op permissions and there are many exceptions.
+
+### Signature permissions
+
+Only apps signed with the defining app's certificate will be granted the permission. This is
+used to restrict APIs to apps of the same developer.
+
+This is frequently used to restrict permissions defined by the platform to apps also signed with
+the platform's certificate. As this is a very tight restriction this is recommended for
+permissions that are only used by apps built out of AOSP which are signed with the platform
+certificate.
+
+Please note that OEMs sign their platform them self. I.e. OEMs can implement new apps using these
+permissions. It is unlikely that 3rd party apps will be able to use APIs protected by signature
+permissions as they are usually not signed with the platform certificate.
+
+Such permissions are defined and checked like an install time permission.
+
+### Preinstalled permissions
+
+This means that the app has to be pre-installed. There is no restriction what apps are pre-installed
+on a particular device install there. Hence it can be really any app including 3rd party apps.
+
+Hence this permission level is discouraged unless there are
+[further restrictions](#restricted-by-tests).
+
+Such permissions are defined and checked like an install time permission.
+
+### Privileged permissions
+
+This means that the app has to be pre-installed and in the `system/priv` directory in the
+filesystem. There is no restriction what apps are in this directory on a particular device
+install there. Hence it can be really any app including 3rd party apps.
+
+An app is only ever granted privileged permissions requested by the pre-installed apk. I.e.
+privileged permissions added in updates will never be granted.
+
+Hence this permission level is discouraged unless there are
+[further restrictions](#restricted-by-tests).
+
+Such permissions are defined and checked like an install time permission.
+
+#### Restricted by tests
+
+As all apps that might get preinstalled or privilidged permissions need to be pre-installed and new
+images need to pass compliance tests it is possible to use a test to whitelist the apps that can
+request the permission.
+
+Example of such a test:
+```kotlin
+/* Add new whitelisted packages to this list */
+private val whitelistedPkgs = listOf("my.whitelisted.package")
+
+@Test
+fun onlySomeAppsAreAllowedToHavePermissionGranted() {
+ assertThat(whitelistedPkgs).containsAllIn(
+ context.packageManager.getInstalledPackages(MATCH_ALL)
+ .filter { pkg ->
+ context.checkPermission(android.Manifest.permission.MY_PRIVILEGED_PERMISSION, -1,
+ pkg.applicationInfo.uid) == PERMISSION_GRANTED
+ /* The permission is defined by the system and hence granted to it */
+ }.filter { pkg -> pkg.applicationInfo.uid != SYSTEM_UID }
+ .map { it.packageName }
+ )
+}
+```
+
+#### Whitelist
+
+As mentioned above it is not suggested, but still common practice to install 3rd party apps as
+privilidged. To verify and restrict which privilidged permissions those apps get granted all
+privilidged permissions need to be explicitly whitelisted in a file `/etc`.
+
+```xml
+<permissions>
+ <privapp-permissions package="my.privileged.package">
+ <!-- allow the app to request a permission -->
+ <permission name="android.permission.MY_PRIVILEGED_PERMISSION"/>
+
+ <!-- Even though the app requests the permission, do not grant it -->
+ <deny-permission name="android.permission.MY_OTHER_PRIVILEGED_PERMISSION"/>
+ </privapp-permissions>
+</permissions>
+```
+
+If the pre-installed apk of app requests a privileged permission that is not mentioned in any
+whitelist or that is not denied the system will refuse to boot. As mentioned above privileged
+permissions added in updates to the pre-installed app will never be granted.
+
+### Limited permissions
+
+E.g. installer, wellbeing, documenter, etc... This allows the system to restrict the permission to a
+well defined app or set of apps. It is possible to add new types in `PackageManagerService`.
+
+Which apps qualify for such a permission level is flexible and custom for each such level. Usually
+they refer to a single or small set of apps, usually - but not always - apps defined in AOSP.
+
+These permissions are defined and checked like an install time permission.
+
+### Development permissions
+
+> Not recommended
+
+By adding the `development` protection level to any permissions the permission can be granted via
+the `pm grant` shell command. This appears to be useful for development and testing, but it is very
+highly discouraged. Any user can grant them permanently via adb, hence adding this tag removes
+all guarantees the permission might otherwise provide.
+
+### Other protection levels
+
+There are other levels (such as `runtime`) but they are for special purposes on should not be
+used by platform developers.
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 1eb7664..dd2ea81 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -16,7 +16,6 @@
package android.provider;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.content.Context;
import android.net.Uri;
@@ -240,7 +239,6 @@
* blocked.
* @hide
*/
- @SystemApi
public static final int STATUS_NOT_BLOCKED = 0;
/**
@@ -248,7 +246,6 @@
* because it is in the list of blocked numbers maintained by the provider.
* @hide
*/
- @SystemApi
public static final int STATUS_BLOCKED_IN_LIST = 1;
/**
@@ -256,7 +253,6 @@
* because it is from a restricted number.
* @hide
*/
- @SystemApi
public static final int STATUS_BLOCKED_RESTRICTED = 2;
/**
@@ -264,7 +260,6 @@
* because it is from an unknown number.
* @hide
*/
- @SystemApi
public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3;
/**
@@ -272,7 +267,6 @@
* because it is from a pay phone.
* @hide
*/
- @SystemApi
public static final int STATUS_BLOCKED_PAYPHONE = 4;
/**
@@ -280,14 +274,12 @@
* because it is from a number not in the users contacts.
* @hide
*/
- @SystemApi
public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
/**
* Integer reason indicating whether a call was blocked, and if so why.
* @hide
*/
- @SystemApi
public static final String RES_BLOCK_STATUS = "block_status";
/** @hide */
@@ -298,31 +290,6 @@
"can_current_user_block_numbers";
/** @hide */
- @SystemApi
- public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
-
- /** @hide */
- public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
-
- /** @hide */
- @SystemApi
- public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
-
- /** @hide */
- public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
- "get_block_suppression_status";
-
- /** @hide */
- public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
- "should_show_emergency_call_notification";
-
- /** @hide */
- public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
-
- /** @hide */
- public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
-
- /** @hide */
public static final String RES_CAN_BLOCK_NUMBERS = "can_block";
/** @hide */
@@ -439,11 +406,26 @@
public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED =
"android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED";
+ public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
+
+ public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
+
+ public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
+
+ public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
+ "get_block_suppression_status";
+
+ public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
+ "should_show_emergency_call_notification";
+
public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed";
public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP =
"blocking_suppressed_until_timestamp";
+ public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
+ public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
+
/* Preference key of block numbers not in contacts setting. */
public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED =
"block_numbers_not_in_contacts_setting";
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index aa511cc..fb81d67 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -399,6 +399,13 @@
public static final String NAMESPACE_CONNECTIVITY_THERMAL_POWER_MANAGER =
"connectivity_thermal_power_manager";
+ /**
+ * Namespace for configuration related features.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_CONFIGURATION = "configuration";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index a80153d..327bca2 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1081,7 +1081,7 @@
// signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
// MANAGE_DOCUMENTS or associated URI permission here instead
final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
- enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingFeatureId(),
+ enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingAttributionTag(),
null);
final String rootId = DocumentsContract.getRootId(rootUri);
@@ -1103,8 +1103,8 @@
enforceTree(documentUri);
if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
- enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
+ enforceReadPermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String childAuthority = childUri.getAuthority();
@@ -1116,8 +1116,8 @@
&& isChildDocument(documentId, childId));
} else if (METHOD_CREATE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -1131,8 +1131,8 @@
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
} else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS);
final IntentSender intentSender = createWebLinkIntent(documentId, options);
@@ -1140,8 +1140,8 @@
out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender);
} else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
final String newDocumentId = renameDocument(documentId, displayName);
@@ -1165,8 +1165,8 @@
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
deleteDocument(documentId);
// Document no longer exists, clean up any grants.
@@ -1176,9 +1176,9 @@
final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(),
+ enforceReadPermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
+ enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(),
null);
final String newDocumentId = copyDocument(documentId, targetId);
@@ -1202,11 +1202,11 @@
final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(),
- null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(),
+ enforceWritePermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
+ enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
+ enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(),
null);
final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
@@ -1228,10 +1228,10 @@
final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(),
- null);
- enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
+ enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
removeDocument(documentId, parentSourceId);
// It's responsibility of the provider to revoke any grants, as the document may be
@@ -1240,8 +1240,8 @@
final boolean isTreeUri = isTreeUri(documentUri);
if (isTreeUri) {
- enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
- null);
+ enforceReadPermissionInner(documentUri, getCallingPackage(),
+ getCallingAttributionTag(), null);
} else {
getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 084f37f..ff34055 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -710,10 +710,7 @@
* If not specified, the default behavior is
* {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_WEAK}.
* <p>
- * Output: Returns {@link android.app.Activity#RESULT_CANCELED} if the user already has an
- * authenticator that meets the requirements, or if the device cannot fulfill the request
- * (e.g. does not have biometric hardware). Returns {@link android.app.Activity#RESULT_OK}
- * otherwise. Note that callers should still check
+ * Output: Nothing. Note that callers should still check
* {@link android.hardware.biometrics.BiometricManager#canAuthenticate(int)}
* afterwards to ensure that the user actually completed enrollment.
*/
@@ -2627,7 +2624,7 @@
arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
- cp.call(cr.getPackageName(), cr.getFeatureId(),
+ cp.call(cr.getPackageName(), cr.getAttributionTag(),
mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
@@ -2647,7 +2644,7 @@
args.putString(CALL_METHOD_PREFIX_KEY, prefix);
args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
IContentProvider cp = mProviderHolder.getProvider(cr);
- Bundle bundle = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ Bundle bundle = cp.call(cr.getPackageName(), cr.getAttributionTag(),
mProviderHolder.mUri.getAuthority(),
mCallSetAllCommand, null, args);
return bundle.getBoolean(KEY_CONFIG_SET_RETURN);
@@ -2722,14 +2719,14 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
mProviderHolder.mUri.getAuthority(), mCallGetCommand, name,
args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
}
if (b != null) {
@@ -2799,13 +2796,13 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri,
+ c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
SELECT_VALUE_PROJECTION, queryArgs, null);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri,
+ c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
SELECT_VALUE_PROJECTION, queryArgs, null);
}
if (c == null) {
@@ -2898,7 +2895,7 @@
}
// Fetch all flags for the namespace at once for caching purposes
- Bundle b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ Bundle b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
if (b == null) {
// Invalid response, return an empty map
@@ -5543,7 +5540,7 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
@@ -6093,10 +6090,7 @@
* device is removed from this mode.
* <p>
* Type: int (0 for false, 1 for true)
- *
- * @hide
*/
- @SystemApi
public static final String SECURE_FRP_MODE = "secure_frp_mode";
/**
@@ -8503,7 +8497,6 @@
*
* @hide
*/
- @SystemApi
public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
/**
@@ -13389,7 +13382,7 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
@@ -14199,19 +14192,6 @@
"power_button_suppression_delay_after_gesture_wake";
/**
- * An integer indicating whether the device is in Common Criteria mode. When enabled,
- * certain device functionalities are tuned to meet the higher security level required
- * by Common Criteria certification. Examples include:
- * Bluetooth long term key material is additionally integrity-protected with AES-GCM.
- * WiFi configuration store is additionally integrity-protected with AES-GCM.
- * A value of 0 means Common Criteria mode is not enabled (default), a value of non-zero
- * means Common Criteria mode is enabled.
- * @hide
- */
- @SystemApi
- public static final String COMMON_CRITERIA_MODE = "common_criteria_mode";
-
- /**
* The usage amount of advanced battery. The value is 0~100.
*
* @hide
@@ -14364,7 +14344,7 @@
arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace));
}
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e);
@@ -14393,7 +14373,7 @@
arg.putInt(CALL_METHOD_USER_KEY, userHandle);
arg.putParcelable(CALL_METHOD_MONITOR_CALLBACK_KEY, callback);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
sProviderHolder.mUri.getAuthority(),
CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
} catch (RemoteException e) {
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 03b38ab..17e3748 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1391,7 +1391,6 @@
* Base column for the table that contain Carrier Public key.
* @hide
*/
- @SystemApi
public interface CarrierColumns extends BaseColumns {
/**
@@ -3551,7 +3550,6 @@
* can manage DPC-owned APNs.
* @hide
*/
- @SystemApi
public static final @NonNull Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
/**
@@ -3864,7 +3862,6 @@
* Integer value denoting an invalid APN id
* @hide
*/
- @SystemApi
public static final int INVALID_APN_ID = -1;
/**
@@ -4706,7 +4703,6 @@
* Contains mappings between matching rules with carrier id for all carriers.
* @hide
*/
- @SystemApi
public static final class All implements BaseColumns {
/**
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 7f68d91..90c934d 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -160,7 +160,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION)
public boolean reset() {
if (!mService.isConnected()) {
Log.e(TAG, "service is not connected");
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 886b433..08aa534 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -232,22 +232,6 @@
* Creates a new builder.
*
* @param presentation The presentation used to visualize this dataset.
- * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
- * as inline suggestions. If the dataset supports inline suggestions,
- * this should not be null.
- */
- public Builder(@NonNull RemoteViews presentation,
- @NonNull InlinePresentation inlinePresentation) {
- Preconditions.checkNotNull(presentation, "presentation must be non-null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
- mPresentation = presentation;
- mInlinePresentation = inlinePresentation;
- }
-
- /**
- * Creates a new builder.
- *
- * @param presentation The presentation used to visualize this dataset.
*/
public Builder(@NonNull RemoteViews presentation) {
Preconditions.checkNotNull(presentation, "presentation must be non-null");
@@ -282,6 +266,22 @@
}
/**
+ * Sets the {@link InlinePresentation} used to visualize this dataset as inline suggestions.
+ * If the dataset supports inline suggestions this should not be null.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
+ * @return this builder.
+ */
+ public @NonNull Builder setInlinePresentation(
+ @NonNull InlinePresentation inlinePresentation) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+ mInlinePresentation = inlinePresentation;
+ return this;
+ }
+
+ /**
* Triggers a custom UI before before autofilling the screen with the contents of this
* dataset.
*
@@ -600,7 +600,7 @@
*/
@SystemApi
@TestApi
- public @NonNull Builder setInlinePresentation(@NonNull AutofillId id,
+ public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id,
@Nullable AutofillValue value, @Nullable Pattern filter,
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
@@ -700,7 +700,7 @@
final Builder builder = presentation != null
? inlinePresentation == null
? new Builder(presentation)
- : new Builder(presentation, inlinePresentation)
+ : new Builder(presentation).setInlinePresentation(inlinePresentation)
: inlinePresentation == null
? new Builder()
: new Builder(inlinePresentation);
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 72e9ad0..8f858d5 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -77,6 +77,15 @@
*/
public static final @RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2;
+ /**
+ * Indicates the request came from a password field.
+ *
+ * (TODO: b/141703197) Temporary fix for augmented autofill showing passwords.
+ *
+ * @hide
+ */
+ public static final @RequestFlags int FLAG_PASSWORD_INPUT_TYPE = 0x4;
+
/** @hide */
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
@@ -149,7 +158,8 @@
/** @hide */
@IntDef(flag = true, prefix = "FLAG_", value = {
FLAG_MANUAL_REQUEST,
- FLAG_COMPATIBILITY_MODE_REQUEST
+ FLAG_COMPATIBILITY_MODE_REQUEST,
+ FLAG_PASSWORD_INPUT_TYPE
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
@@ -169,6 +179,8 @@
return "FLAG_MANUAL_REQUEST";
case FLAG_COMPATIBILITY_MODE_REQUEST:
return "FLAG_COMPATIBILITY_MODE_REQUEST";
+ case FLAG_PASSWORD_INPUT_TYPE:
+ return "FLAG_PASSWORD_INPUT_TYPE";
default: return Integer.toHexString(value);
}
}
@@ -223,7 +235,8 @@
Preconditions.checkFlagsArgument(
mFlags,
FLAG_MANUAL_REQUEST
- | FLAG_COMPATIBILITY_MODE_REQUEST);
+ | FLAG_COMPATIBILITY_MODE_REQUEST
+ | FLAG_PASSWORD_INPUT_TYPE);
this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
onConstructed();
@@ -352,7 +365,8 @@
Preconditions.checkFlagsArgument(
mFlags,
FLAG_MANUAL_REQUEST
- | FLAG_COMPATIBILITY_MODE_REQUEST);
+ | FLAG_COMPATIBILITY_MODE_REQUEST
+ | FLAG_PASSWORD_INPUT_TYPE);
this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
onConstructed();
@@ -373,10 +387,10 @@
};
@DataClass.Generated(
- time = 1575928271155L,
+ time = 1583196707026L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
- inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+ inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
index 1011651..1bcc76b 100644
--- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -25,7 +25,8 @@
* @hide
*/
oneway interface IInlineSuggestionUiCallback {
- void onAutofill();
+ void onClick();
+ void onLongClick();
void onContent(in SurfaceControlViewHost.SurfacePackage surface);
void onError();
void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index ee15283..7fbc309 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -94,15 +94,30 @@
final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(),
hostInputToken);
- host.addView(suggestionRoot, lp);
+ host.setView(suggestionRoot, lp);
suggestionRoot.setOnClickListener((v) -> {
try {
- callback.onAutofill();
+ if (suggestionView.hasOnClickListeners()) {
+ suggestionView.callOnClick();
+ }
+ callback.onClick();
} catch (RemoteException e) {
- Log.w(TAG, "RemoteException calling onAutofill()");
+ Log.w(TAG, "RemoteException calling onClick()");
}
});
+ suggestionRoot.setOnLongClickListener((v) -> {
+ try {
+ if (suggestionView.hasOnLongClickListeners()) {
+ suggestionView.performLongClick();
+ }
+ callback.onLongClick();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onLongClick()");
+ }
+ return true;
+ });
+
sendResult(callback, host.getSurfacePackage());
} finally {
updateDisplay(Display.DEFAULT_DISPLAY);
diff --git a/core/java/android/service/autofill/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
index bdcc253..6c9d36b 100644
--- a/core/java/android/service/autofill/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -52,6 +52,11 @@
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return true;
+ }
+
+ @Override
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(@NonNull MotionEvent event) {
switch (event.getActionMasked()) {
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index ed27dd5..5b08ae2 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -488,7 +488,8 @@
ids.add(pair.first);
values.add(pair.second);
}
- mClient.autofill(mSessionId, ids, values);
+ final boolean hideHighlight = size == 1 && ids.get(0).equals(mFocusedId);
+ mClient.autofill(mSessionId, ids, values, hideHighlight);
}
public void setFillWindow(@NonNull FillWindow fillWindow) {
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 0b9a8af..d4db79e 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -206,6 +207,7 @@
* @throws IOException if trouble opening the file for writing, such as lack of disk space
* or unavailable media.
*/
+ @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
public void writeData(@NonNull String name, long offsetBytes, long lengthBytes,
@NonNull ParcelFileDescriptor incomingFd) throws IOException {
try {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 0cd96b8..c52b02b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -55,7 +55,6 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Slog;
import android.widget.RemoteViews;
import com.android.internal.annotations.GuardedBy;
@@ -1570,6 +1569,7 @@
private boolean mVisuallyInterruptive;
private boolean mIsConversation;
private ShortcutInfo mShortcutInfo;
+ private boolean mIsBubble;
private static final int PARCEL_VERSION = 2;
@@ -1604,6 +1604,7 @@
out.writeBoolean(mVisuallyInterruptive);
out.writeBoolean(mIsConversation);
out.writeParcelable(mShortcutInfo, flags);
+ out.writeBoolean(mIsBubble);
}
/** @hide */
@@ -1639,6 +1640,7 @@
mVisuallyInterruptive = in.readBoolean();
mIsConversation = in.readBoolean();
mShortcutInfo = in.readParcelable(cl);
+ mIsBubble = in.readBoolean();
}
@@ -1844,6 +1846,14 @@
}
/**
+ * Returns whether this notification is actively a bubble.
+ * @hide
+ */
+ public boolean isBubble() {
+ return mIsBubble;
+ }
+
+ /**
* @hide
*/
public @Nullable ShortcutInfo getShortcutInfo() {
@@ -1862,7 +1872,8 @@
int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
boolean noisy, ArrayList<Notification.Action> smartActions,
ArrayList<CharSequence> smartReplies, boolean canBubble,
- boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo) {
+ boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo,
+ boolean isBubble) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1886,6 +1897,7 @@
mVisuallyInterruptive = visuallyInterruptive;
mIsConversation = isConversation;
mShortcutInfo = shortcutInfo;
+ mIsBubble = isBubble;
}
/**
@@ -1913,7 +1925,8 @@
other.mCanBubble,
other.mVisuallyInterruptive,
other.mIsConversation,
- other.mShortcutInfo);
+ other.mShortcutInfo,
+ other.mIsBubble);
}
/**
@@ -1970,7 +1983,8 @@
&& Objects.equals(mIsConversation, other.mIsConversation)
// Shortcutinfo doesn't have equals either; use id
&& Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()),
- (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()));
+ (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()))
+ && Objects.equals(mIsBubble, other.mIsBubble);
}
}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
index d2494a5..ae67068 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
@@ -24,8 +24,6 @@
import android.text.TextUtils;
import android.util.Log;
-import java.util.List;
-
/**
* Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
*
@@ -56,7 +54,6 @@
* presented as the selected card.
*/
public void onSuccess(@NonNull GetWalletCardsResponse response) {
- Log.i(TAG, "onSuccess");
if (isValidResponse(response)) {
mHandler.post(() -> onSuccessInternal(response));
} else {
@@ -78,7 +75,6 @@
}
private void onSuccessInternal(GetWalletCardsResponse response) {
- Log.i(TAG, "onSuccessInternal");
if (mCalled) {
Log.w(TAG, "already called");
return;
@@ -86,7 +82,6 @@
mCalled = true;
try {
mCallback.onGetWalletCardsSuccess(response);
- Log.i(TAG, "onSuccessInternal: returned response");
} catch (RemoteException e) {
Log.w(TAG, "Error returning wallet cards", e);
}
@@ -106,29 +101,53 @@
}
private boolean isValidResponse(@NonNull GetWalletCardsResponse response) {
- return response != null
- && response.getWalletCards() != null
- && response.getSelectedIndex() >= 0
- && (response.getWalletCards().isEmpty() // selectedIndex may be 0 when list is empty
- || response.getSelectedIndex() < response.getWalletCards().size())
- && response.getWalletCards().size() < mRequest.getMaxCards()
- && areValidCards(response.getWalletCards());
- }
-
- private boolean areValidCards(List<WalletCard> walletCards) {
- for (WalletCard walletCard : walletCards) {
- if (walletCard == null
- || walletCard.getCardId() == null
- || walletCard.getCardImage() == null
- || TextUtils.isEmpty(walletCard.getContentDescription())
- || walletCard.getPendingIntent() == null) {
+ if (response == null) {
+ Log.w(TAG, "Invalid response: response is null");
+ return false;
+ }
+ if (response.getWalletCards() == null) {
+ Log.w(TAG, "Invalid response: walletCards is null");
+ return false;
+ }
+ if (response.getSelectedIndex() < 0) {
+ Log.w(TAG, "Invalid response: selectedIndex is negative");
+ return false;
+ }
+ if (!response.getWalletCards().isEmpty()
+ && response.getSelectedIndex() >= response.getWalletCards().size()) {
+ Log.w(TAG, "Invalid response: selectedIndex out of bounds");
+ return false;
+ }
+ if (response.getWalletCards().size() > mRequest.getMaxCards()) {
+ Log.w(TAG, "Invalid response: too many cards");
+ return false;
+ }
+ for (WalletCard walletCard : response.getWalletCards()) {
+ if (walletCard == null) {
+ Log.w(TAG, "Invalid response: card is null");
+ return false;
+ }
+ if (walletCard.getCardId() == null) {
+ Log.w(TAG, "Invalid response: cardId is null");
return false;
}
Icon cardImage = walletCard.getCardImage();
+ if (cardImage == null) {
+ Log.w(TAG, "Invalid response: cardImage is null");
+ return false;
+ }
if (cardImage.getType() == Icon.TYPE_BITMAP
- && walletCard.getCardImage().getBitmap().getConfig()
- != Bitmap.Config.HARDWARE) {
- Log.w(TAG, "WalletCard bitmaps should be hardware bitmaps");
+ && cardImage.getBitmap().getConfig() != Bitmap.Config.HARDWARE) {
+ Log.w(TAG, "Invalid response: cardImage bitmaps must be hardware bitmaps");
+ return false;
+ }
+ if (TextUtils.isEmpty(walletCard.getContentDescription())) {
+ Log.w(TAG, "Invalid response: contentDescription is null");
+ return false;
+ }
+ if (walletCard.getPendingIntent() == null) {
+ Log.w(TAG, "Invalid response: pendingIntent is null");
+ return false;
}
}
return true;
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index be9ab11..4f5b139 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -16,19 +16,23 @@
package android.service.quickaccesswallet;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
import android.content.Intent;
+import java.io.Closeable;
+import java.util.concurrent.Executor;
+
/**
* Facilitates accessing cards from the {@link QuickAccessWalletService}.
*
* @hide
*/
@TestApi
-public interface QuickAccessWalletClient {
+public interface QuickAccessWalletClient extends Closeable {
/**
* Create a client for accessing wallet cards from the {@link QuickAccessWalletService}. If the
@@ -92,6 +96,14 @@
@NonNull OnWalletCardsRetrievedCallback callback);
/**
+ * Get wallet cards from the {@link QuickAccessWalletService}.
+ */
+ void getWalletCards(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GetWalletCardsRequest request,
+ @NonNull OnWalletCardsRetrievedCallback callback);
+
+ /**
* Callback for getWalletCards
*/
interface OnWalletCardsRetrievedCallback {
@@ -111,12 +123,19 @@
void notifyWalletDismissed();
/**
- * Unregister event listener.
+ * Register an event listener.
*/
void addWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
/**
- * Unregister event listener
+ * Register an event listener.
+ */
+ void addWalletServiceEventListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull WalletServiceEventListener listener);
+
+ /**
+ * Unregister an event listener
*/
void removeWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 37a8703..e4dd082 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -18,6 +18,7 @@
import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE_INTERFACE;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -36,16 +37,19 @@
import com.android.internal.widget.LockPatternUtils;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
+import java.util.concurrent.Executor;
/**
* Implements {@link QuickAccessWalletClient}. The client connects, performs requests, waits for
- * responses, and disconnects automatically after a short period of time. The client may
+ * responses, and disconnects automatically one minute after the last call is performed.
+ *
* @hide
*/
public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
@@ -78,15 +82,14 @@
@Override
public boolean isWalletServiceAvailable() {
- boolean available = mServiceInfo != null;
- Log.i(TAG, "isWalletServiceAvailable: " + available);
- return available;
+ return mServiceInfo != null;
}
@Override
public boolean isWalletFeatureAvailable() {
int currentUser = ActivityManager.getCurrentUser();
- return checkUserSetupComplete()
+ return currentUser == UserHandle.USER_SYSTEM
+ && checkUserSetupComplete()
&& checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
&& !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
}
@@ -101,23 +104,29 @@
public void getWalletCards(
@NonNull GetWalletCardsRequest request,
@NonNull OnWalletCardsRetrievedCallback callback) {
+ getWalletCards(mContext.getMainExecutor(), request, callback);
+ }
- Log.i(TAG, "getWalletCards");
-
+ @Override
+ public void getWalletCards(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GetWalletCardsRequest request,
+ @NonNull OnWalletCardsRetrievedCallback callback) {
if (!isWalletServiceAvailable()) {
- callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null));
+ executor.execute(
+ () -> callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null)));
return;
}
BaseCallbacks serviceCallback = new BaseCallbacks() {
@Override
public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
- mHandler.post(() -> callback.onWalletCardsRetrieved(response));
+ executor.execute(() -> callback.onWalletCardsRetrieved(response));
}
@Override
public void onGetWalletCardsFailure(GetWalletCardsError error) {
- mHandler.post(() -> callback.onWalletCardRetrievalError(error));
+ executor.execute(() -> callback.onWalletCardRetrievalError(error));
}
};
@@ -132,11 +141,11 @@
serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
}
});
+
}
@Override
public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
- Log.i(TAG, "selectWalletCard");
if (!isWalletServiceAvailable()) {
return;
}
@@ -153,7 +162,6 @@
if (!isWalletServiceAvailable()) {
return;
}
- Log.i(TAG, "notifyWalletDismissed");
executeApiCall(new ApiCaller("onWalletDismissed") {
@Override
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
@@ -164,15 +172,20 @@
@Override
public void addWalletServiceEventListener(WalletServiceEventListener listener) {
+ addWalletServiceEventListener(mContext.getMainExecutor(), listener);
+ }
+
+ @Override
+ public void addWalletServiceEventListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull WalletServiceEventListener listener) {
if (!isWalletServiceAvailable()) {
return;
}
- Log.i(TAG, "registerWalletServiceEventListener");
BaseCallbacks callback = new BaseCallbacks() {
@Override
public void onWalletServiceEvent(WalletServiceEvent event) {
- Log.i(TAG, "onWalletServiceEvent");
- mHandler.post(() -> listener.onWalletServiceEvent(event));
+ executor.execute(() -> listener.onWalletServiceEvent(event));
}
};
@@ -193,7 +206,6 @@
if (!isWalletServiceAvailable()) {
return;
}
- Log.i(TAG, "unregisterWalletServiceEventListener");
executeApiCall(new ApiCaller("unregisterListener") {
@Override
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
@@ -209,8 +221,12 @@
}
@Override
+ public void close() throws IOException {
+ disconnect();
+ }
+
+ @Override
public void disconnect() {
- Log.i(TAG, "disconnect");
mHandler.post(() -> disconnectInternal(true));
}
@@ -241,18 +257,15 @@
}
private void connect() {
- Log.i(TAG, "connect");
mHandler.post(this::connectInternal);
}
private void connectInternal() {
- Log.i(TAG, "connectInternal");
if (mServiceInfo == null) {
Log.w(TAG, "Wallet service unavailable");
return;
}
if (mIsConnected) {
- Log.w(TAG, "already connected");
return;
}
mIsConnected = true;
@@ -264,23 +277,14 @@
}
private void onConnectedInternal(IQuickAccessWalletService service) {
- Log.i(TAG, "onConnectedInternal");
if (!mIsConnected) {
Log.w(TAG, "onConnectInternal but connection closed");
mService = null;
return;
}
mService = service;
- Log.i(TAG, "onConnectedInternal success: request queue size " + mRequestQueue.size());
for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
- try {
- apiCaller.performApiCall(mService);
- } catch (RemoteException e) {
- Log.e(TAG, "onConnectedInternal error", e);
- apiCaller.onApiError();
- disconnect();
- break;
- }
+ performApiCallInternal(apiCaller, mService);
mRequestQueue.remove(apiCaller);
}
}
@@ -290,7 +294,6 @@
* posting a new delayed message.
*/
private void resetServiceConnectionTimeout() {
- Log.i(TAG, "resetServiceConnectionTimeout");
mHandler.removeMessages(MSG_TIMEOUT_SERVICE);
mHandler.postDelayed(
() -> disconnectInternal(true),
@@ -299,13 +302,11 @@
}
private void disconnectInternal(boolean clearEventListeners) {
- Log.i(TAG, "disconnectInternal: " + clearEventListeners);
if (!mIsConnected) {
Log.w(TAG, "already disconnected");
return;
}
if (clearEventListeners && !mEventListeners.isEmpty()) {
- Log.i(TAG, "disconnectInternal: clear event listeners");
for (WalletServiceEventListener listener : mEventListeners.keySet()) {
removeWalletServiceEventListener(listener);
}
@@ -320,29 +321,33 @@
}
private void executeApiCall(ApiCaller apiCaller) {
- Log.i(TAG, "execute: " + apiCaller.mDesc);
mHandler.post(() -> executeInternal(apiCaller));
}
- private void executeInternal(ApiCaller apiCall) {
- Log.i(TAG, "executeInternal: " + apiCall.mDesc);
+ private void executeInternal(ApiCaller apiCaller) {
if (mIsConnected && mService != null) {
- try {
- apiCall.performApiCall(mService);
- Log.i(TAG, "executeInternal success: " + apiCall.mDesc);
- resetServiceConnectionTimeout();
- } catch (RemoteException e) {
- Log.w(TAG, "executeInternal error: " + apiCall.mDesc, e);
- apiCall.onApiError();
- disconnect();
- }
+ performApiCallInternal(apiCaller, mService);
} else {
- Log.i(TAG, "executeInternal: queued" + apiCall.mDesc);
- mRequestQueue.add(apiCall);
+ mRequestQueue.add(apiCaller);
connect();
}
}
+ private void performApiCallInternal(ApiCaller apiCaller, IQuickAccessWalletService service) {
+ if (service == null) {
+ apiCaller.onApiError();
+ return;
+ }
+ try {
+ apiCaller.performApiCall(service);
+ resetServiceConnectionTimeout();
+ } catch (RemoteException e) {
+ Log.w(TAG, "executeInternal error: " + apiCaller.mDesc, e);
+ apiCaller.onApiError();
+ disconnect();
+ }
+ }
+
private abstract static class ApiCaller {
private final String mDesc;
@@ -350,7 +355,8 @@
this.mDesc = desc;
}
- abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
+ abstract void performApiCall(IQuickAccessWalletService service)
+ throws RemoteException;
void onApiError() {
Log.w(TAG, "api error: " + mDesc);
@@ -359,7 +365,6 @@
@Override // ServiceConnection
public void onServiceConnected(ComponentName name, IBinder binder) {
- Log.i(TAG, "onServiceConnected: " + name);
IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
mHandler.post(() -> onConnectedInternal(service));
}
@@ -367,19 +372,16 @@
@Override // ServiceConnection
public void onServiceDisconnected(ComponentName name) {
// Do not disconnect, as we may later be re-connected
- Log.w(TAG, "onServiceDisconnected");
}
@Override // ServiceConnection
public void onBindingDied(ComponentName name) {
// This is a recoverable error but the client will need to reconnect.
- Log.w(TAG, "onBindingDied");
disconnect();
}
@Override // ServiceConnection
public void onNullBinding(ComponentName name) {
- Log.w(TAG, "onNullBinding");
disconnect();
}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 23173a8..31e51bb 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -66,13 +66,11 @@
static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) {
ComponentName defaultPaymentApp = getDefaultPaymentApp(context);
if (defaultPaymentApp == null) {
- Log.d(TAG, "create: default payment app not set");
return null;
}
ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultPaymentApp.getPackageName());
if (serviceInfo == null) {
- Log.d(TAG, "create: unable to resolve service intent");
return null;
}
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
index e6ae0ab..b09d2e9 100644
--- a/core/java/android/service/quickaccesswallet/WalletCard.java
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -180,7 +180,7 @@
* GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
* GetWalletCardsRequest#getCardHeightPx()}. If the card image
* does not have these dimensions, it may appear distorted when it
- * is scaled to fit these dimensions on screen. Bitmaps should be
+ * is scaled to fit these dimensions on screen. Bitmaps must be
* of type {@link android.graphics.Bitmap.Config#HARDWARE} for
* performance reasons.
* @param contentDescription The content description of the card image. This field is
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 3ff6f54..9dfbc28 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -41,7 +41,6 @@
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassificationConstants;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
@@ -405,27 +404,19 @@
*/
@NonNull
public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
- final TextClassificationManager tcm =
- context.getSystemService(TextClassificationManager.class);
- if (tcm == null) {
+ final String defaultTextClassifierPackageName =
+ context.getPackageManager().getDefaultTextClassifierPackageName();
+ if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
return TextClassifier.NO_OP;
}
- TextClassificationConstants settings = new TextClassificationConstants();
- if (settings.getUseDefaultTextClassifierAsDefaultImplementation()) {
- final String defaultTextClassifierPackageName =
- context.getPackageManager().getDefaultTextClassifierPackageName();
- if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
- return TextClassifier.NO_OP;
- }
- if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
- throw new RuntimeException(
- "The default text classifier itself should not call the"
- + "getDefaultTextClassifierImplementation() method.");
- }
- return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
- } else {
- return tcm.getTextClassifier(TextClassifier.LOCAL);
+ if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
+ throw new RuntimeException(
+ "The default text classifier itself should not call the"
+ + "getDefaultTextClassifierImplementation() method.");
}
+ final TextClassificationManager tcm =
+ context.getSystemService(TextClassificationManager.class);
+ return tcm.getTextClassifier(TextClassifier.DEFAULT_SYSTEM);
}
/** @hide **/
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 8e6f77b..4a0dd87 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1314,7 +1314,8 @@
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(mContext);
int res = mSystemService.startVoiceActivity(mToken, intent,
- intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
+ intent.resolveType(mContext.getContentResolver()),
+ mContext.getAttributionTag());
Instrumentation.checkStartActivityResult(res, intent);
} catch (RemoteException e) {
}
@@ -1342,7 +1343,8 @@
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(mContext);
int res = mSystemService.startAssistantActivity(mToken, intent,
- intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
+ intent.resolveType(mContext.getContentResolver()),
+ mContext.getAttributionTag());
Instrumentation.checkStartActivityResult(res, intent);
} catch (RemoteException e) {
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index bf3c088..f531e12 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -124,8 +124,6 @@
private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050;
private static final int MSG_SCALE = 10100;
- private static final float MAX_SCALE = 1.15f;
-
private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
private final ArrayList<Engine> mActiveEngines
@@ -351,7 +349,7 @@
@Override
public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
- boolean sync) {
+ float zoom, boolean sync) {
synchronized (mLock) {
if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
mPendingXOffset = x;
@@ -366,6 +364,8 @@
Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
mCaller.sendMessage(msg);
}
+ Message msg = mCaller.obtainMessageI(MSG_SCALE, Float.floatToIntBits(zoom));
+ mCaller.sendMessage(msg);
}
}
@@ -462,6 +462,18 @@
}
/**
+ * This will be called when the wallpaper is first started. If true is returned, the system
+ * will zoom in the wallpaper by default and zoom it out as the user interacts,
+ * to create depth. Otherwise, zoom will have to be handled manually
+ * in {@link #onZoomChanged(float)}.
+ *
+ * @hide
+ */
+ public boolean shouldZoomOutWallpaper() {
+ return false;
+ }
+
+ /**
* Control whether this wallpaper will receive raw touch events
* from the window manager as the user interacts with the window
* that is currently displaying the wallpaper. By default they
@@ -870,6 +882,7 @@
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
}
+ mSession.setShouldZoomOutWallpaper(mWindow, shouldZoomOutWallpaper());
mCreated = true;
mInputEventReceiver = new WallpaperInputEventReceiver(
@@ -964,7 +977,6 @@
c.surfaceCreated(mSurfaceHolder);
}
}
- onZoomChanged(0f);
}
redrawNeeded |= creating || (relayoutResult
@@ -1125,7 +1137,6 @@
mIsInAmbientMode = inAmbientMode;
if (mCreated) {
onAmbientModeChanged(inAmbientMode, animationDuration);
- setZoom(0);
}
}
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index e93ba16..92f3538 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -342,7 +342,7 @@
}
try {
mService.startListening(recognizerIntent, mListener, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
if (DBG) Log.d(TAG, "service start listening command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "startListening() failed", e);
@@ -357,7 +357,7 @@
}
try {
mService.stopListening(mListener, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
if (DBG) Log.d(TAG, "service stop listening command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "stopListening() failed", e);
@@ -371,7 +371,7 @@
return;
}
try {
- mService.cancel(mListener, mContext.getOpPackageName(), mContext.getFeatureId());
+ mService.cancel(mListener, mContext.getOpPackageName(), mContext.getAttributionTag());
if (DBG) Log.d(TAG, "service cancel command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "cancel() failed", e);
@@ -400,7 +400,8 @@
public void destroy() {
if (mService != null) {
try {
- mService.cancel(mListener, mContext.getOpPackageName(), mContext.getFeatureId());
+ mService.cancel(mListener, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
} catch (final RemoteException e) {
// Not important
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index d273500..28db270 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -176,7 +176,6 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
- @SystemApi
public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200;
/**
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 7238b12..ab9df56 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -116,7 +116,7 @@
mSubscriptionChangedListenerMap.put(listener, callback);
try {
sRegistry.addOnSubscriptionsChangedListener(mContext.getOpPackageName(),
- mContext.getFeatureId(), callback);
+ mContext.getAttributionTag(), callback);
} catch (RemoteException ex) {
// system server crash
}
@@ -175,7 +175,7 @@
mOpportunisticSubscriptionChangedListenerMap.put(listener, callback);
try {
sRegistry.addOnOpportunisticSubscriptionsChangedListener(mContext.getOpPackageName(),
- mContext.getFeatureId(), callback);
+ mContext.getAttributionTag(), callback);
} catch (RemoteException ex) {
// system server crash
}
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 2aca36a..82c7ea7 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.telephony.PhoneNumberUtils;
@@ -663,9 +664,11 @@
private static void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s,
@Nullable Context context) {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
+ final Context ctx = (context != null) ? context : ActivityThread.currentApplication();
+ final String regionCode = (ctx != null) ? ctx.getSystemService(TelephonyManager.class).
+ getSimCountryIso().toUpperCase(Locale.US) : Locale.getDefault().getCountry();
Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
- TelephonyManager.getDefaultSimCountryIso().toUpperCase(Locale.US),
- Leniency.POSSIBLE, Long.MAX_VALUE);
+ regionCode, Leniency.POSSIBLE, Long.MAX_VALUE);
for (PhoneNumberMatch match : matches) {
LinkSpec spec = new LinkSpec();
spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 6b87a10..4dafc0d 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -67,11 +67,11 @@
DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
DEFAULT_FLAGS.put("settings_conditionals", "false");
DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
- // 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");
+ // 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, "true");
- DEFAULT_FLAGS.put("settings_tether_all_in_one", "true");
+ DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false");
DEFAULT_FLAGS.put("settings_contextual_home2", "false");
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index abd04cc..2437af2 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,6 +16,8 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
@@ -211,9 +213,24 @@
verityDigest, apk.length(), signatureInfo);
}
+ result.digest = pickBestV3DigestForV4(contentDigests);
+
return result;
}
+ // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier.
+ private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) {
+ final int[] orderedContentDigestTypes =
+ {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+ CONTENT_DIGEST_CHUNKED_SHA256};
+ for (int contentDigestType : orderedContentDigestTypes) {
+ if (contentDigests.containsKey(contentDigestType)) {
+ return contentDigests.get(contentDigestType);
+ }
+ }
+ return null;
+ }
+
private static VerifiedSigner verifySigner(
ByteBuffer signerBlock,
Map<Integer, byte[]> contentDigests,
@@ -568,6 +585,7 @@
public final VerifiedProofOfRotation por;
public byte[] verityRootHash;
+ public byte[] digest;
public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
this.certs = certs;
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index b6b8089..8c240d9 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -16,13 +16,32 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+
import android.os.incremental.IncrementalManager;
+import android.os.incremental.V4Signature;
+import android.util.Pair;
+import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
import java.security.cert.Certificate;
-
-import sun.security.pkcs.PKCS7;
-import sun.security.pkcs.ParsingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
/**
* APK Signature Scheme v4 verifier.
@@ -30,24 +49,118 @@
* @hide for internal use only.
*/
public class ApkSignatureSchemeV4Verifier {
+ /**
+ * Extracts and verifies APK Signature Scheme v4 signatures of the provided APK and returns the
+ * certificates associated with each signer.
+ */
+ public static VerifiedSigner extractCertificates(String apkFile)
+ throws SignatureNotFoundException, SecurityException {
+ final File apk = new File(apkFile);
+ final byte[] signatureBytes = IncrementalManager.unsafeGetFileSignature(
+ apk.getAbsolutePath());
+ if (signatureBytes == null || signatureBytes.length == 0) {
+ throw new SignatureNotFoundException("Failed to obtain signature bytes from IncFS.");
+ }
+
+ final V4Signature signature;
+ final V4Signature.HashingInfo hashingInfo;
+ final V4Signature.SigningInfo signingInfo;
+ try {
+ signature = V4Signature.readFrom(signatureBytes);
+
+ if (!signature.isVersionSupported()) {
+ throw new SecurityException(
+ "v4 signature version " + signature.version + " is not supported");
+ }
+
+ hashingInfo = V4Signature.HashingInfo.fromByteArray(signature.hashingInfo);
+ signingInfo = V4Signature.SigningInfo.fromByteArray(signature.signingInfo);
+ } catch (IOException e) {
+ throw new SignatureNotFoundException("Failed to read V4 signature.", e);
+ }
+
+ final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo,
+ signingInfo);
+
+ return verifySigner(signingInfo, signedData);
+ }
+
+ private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo,
+ final byte[] signedData) throws SecurityException {
+ if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) {
+ throw new SecurityException("No supported signatures found");
+ }
+
+ final int signatureAlgorithmId = signingInfo.signatureAlgorithmId;
+ final byte[] signatureBytes = signingInfo.signature;
+ final byte[] publicKeyBytes = signingInfo.publicKey;
+ final byte[] encodedCert = signingInfo.certificate;
+
+ String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(signatureAlgorithmId);
+ Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams =
+ getSignatureAlgorithmJcaSignatureAlgorithm(signatureAlgorithmId);
+ String jcaSignatureAlgorithm = signatureAlgorithmParams.first;
+ AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second;
+ boolean sigVerified;
+ try {
+ PublicKey publicKey =
+ KeyFactory.getInstance(keyAlgorithm)
+ .generatePublic(new X509EncodedKeySpec(publicKeyBytes));
+ Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
+ sig.initVerify(publicKey);
+ if (jcaSignatureAlgorithmParams != null) {
+ sig.setParameter(jcaSignatureAlgorithmParams);
+ }
+ sig.update(signedData);
+ sigVerified = sig.verify(signatureBytes);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
+ | InvalidAlgorithmParameterException | SignatureException e) {
+ throw new SecurityException(
+ "Failed to verify " + jcaSignatureAlgorithm + " signature", e);
+ }
+ if (!sigVerified) {
+ throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
+ }
+
+ // Signature over signedData has verified.
+ CertificateFactory certFactory;
+ try {
+ certFactory = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+ }
+
+ X509Certificate certificate;
+ try {
+ certificate = (X509Certificate)
+ certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
+ } catch (CertificateException e) {
+ throw new SecurityException("Failed to decode certificate", e);
+ }
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
+
+ byte[] certificatePublicKeyBytes = certificate.getPublicKey().getEncoded();
+ if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
+ throw new SecurityException(
+ "Public key mismatch between certificate and signature record");
+ }
+
+ return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest);
+ }
/**
- * Extracts APK Signature Scheme v4 signatures of the provided APK and returns the certificates
- * associated with each signer.
+ * Verified APK Signature Scheme v4 signer, including V3 digest.
+ *
+ * @hide for internal use only.
*/
- public static Certificate[] extractCertificates(String apkFile)
- throws SignatureNotFoundException, SecurityException {
- final byte[] rawSignature = IncrementalManager.unsafeGetFileSignature(
- new File(apkFile).getAbsolutePath());
- if (rawSignature == null || rawSignature.length == 0) {
- throw new SignatureNotFoundException("Failed to obtain raw signature from IncFS.");
+ public static class VerifiedSigner {
+ public final Certificate[] certs;
+ public byte[] v3Digest;
+
+ public VerifiedSigner(Certificate[] certs, byte[] v3Digest) {
+ this.certs = certs;
+ this.v3Digest = v3Digest;
}
- try {
- PKCS7 pkcs7 = new PKCS7(rawSignature);
- return pkcs7.getCertificates();
- } catch (ParsingException e) {
- throw new SecurityException("Failed to parse signature and extract certificates", e);
- }
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index f325c21..c1cee48 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -168,7 +168,7 @@
/**
* Verifies the provided APK using V4 schema.
*
- * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+ * @param verifyFull whether to verify (V4 vs V3) or just collect certificates.
* @return the certificates associated with each signer.
* @throws SignatureNotFoundException if there are no V4 signatures in the APK
* @throws PackageParserException if there was a problem collecting certificates
@@ -178,30 +178,34 @@
throws SignatureNotFoundException, PackageParserException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
try {
- Certificate[] certs = ApkSignatureSchemeV4Verifier.extractCertificates(apkPath);
- Certificate[][] signerCerts = new Certificate[][]{certs};
+ ApkSignatureSchemeV4Verifier.VerifiedSigner vSigner =
+ ApkSignatureSchemeV4Verifier.extractCertificates(apkPath);
+ Certificate[][] signerCerts = new Certificate[][]{vSigner.certs};
Signature[] signerSigs = convertToSignatures(signerCerts);
if (verifyFull) {
- // v4 is an add-on and requires v2/v3 signature to validate against its certificates
- final PackageParser.SigningDetails nonstreaming = verifyV3AndBelowSignatures(
- apkPath, minSignatureSchemeVersion, false);
- if (nonstreaming.signatureSchemeVersion <= SignatureSchemeVersion.JAR) {
+ // v4 is an add-on and requires v3 signature to validate against its certificates
+ ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming =
+ ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
+ Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs};
+ Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
+
+ if (nonstreamingSigs.length != signerSigs.length) {
throw new SecurityException(
- "V4 signing block can only be verified along with V2 and above.");
- }
- if (nonstreaming.signatures.length == 0
- || nonstreaming.signatures.length != signerSigs.length) {
- throw new SecurityException("Invalid number of signatures in "
- + nonstreaming.signatureSchemeVersion);
+ "Invalid number of certificates: " + nonstreaming.certs.length);
}
for (int i = 0, size = signerSigs.length; i < size; ++i) {
- if (!nonstreaming.signatures[i].equals(signerSigs[i])) {
- throw new SecurityException("V4 signature certificate does not match "
- + nonstreaming.signatureSchemeVersion);
+ if (!nonstreamingSigs[i].equals(signerSigs[i])) {
+ throw new SecurityException("V4 signature certificate does not match V3");
}
}
+
+ // TODO(b/151240006): add support for v2 digest and make it mandatory.
+ if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest,
+ nonstreaming.digest, vSigner.v3Digest.length)) {
+ throw new SecurityException("V3 digest in V4 signature does not match V3");
+ }
}
return new PackageParser.SigningDetails(signerSigs,
diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java
index 759c864..a7ae32d 100644
--- a/core/java/android/util/apk/SourceStampVerifier.java
+++ b/core/java/android/util/apk/SourceStampVerifier.java
@@ -24,6 +24,7 @@
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.util.Pair;
+import android.util.Slog;
import android.util.jar.StrictJarFile;
import libcore.io.IoUtils;
@@ -43,6 +44,7 @@
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
+import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -68,6 +70,8 @@
*/
public abstract class SourceStampVerifier {
+ private static final String TAG = "SourceStampVerifier";
+
private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0;
private static final int SOURCE_STAMP_BLOCK_ID = 0x2b09189e;
@@ -78,29 +82,59 @@
/** Hidden constructor to prevent instantiation. */
private SourceStampVerifier() {}
+ /** Verifies SourceStamp present in a list of APKs. */
+ public static SourceStampVerificationResult verify(List<String> apkFiles) {
+ Certificate stampCertificate = null;
+ for (String apkFile : apkFiles) {
+ SourceStampVerificationResult sourceStampVerificationResult = verify(apkFile);
+ if (!sourceStampVerificationResult.isPresent()
+ || !sourceStampVerificationResult.isVerified()) {
+ return sourceStampVerificationResult;
+ }
+ if (stampCertificate != null
+ && !stampCertificate.equals(sourceStampVerificationResult.getCertificate())) {
+ return SourceStampVerificationResult.notVerified();
+ }
+ stampCertificate = sourceStampVerificationResult.getCertificate();
+ }
+ return SourceStampVerificationResult.verified(stampCertificate);
+ }
+
/** Verifies SourceStamp present in the provided APK. */
public static SourceStampVerificationResult verify(String apkFile) {
+ StrictJarFile apkJar = null;
try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
- return verify(apk);
- } catch (Exception e) {
- // Any exception in the SourceStamp verification returns a non-verified SourceStamp
- // outcome without affecting the outcome of any of the other signature schemes.
- return SourceStampVerificationResult.notVerified();
+ apkJar =
+ new StrictJarFile(
+ apkFile,
+ /* verify= */ false,
+ /* signatureSchemeRollbackProtectionsEnforced= */ false);
+ byte[] sourceStampCertificateDigest = getSourceStampCertificateDigest(apkJar);
+ if (sourceStampCertificateDigest == null) {
+ // SourceStamp certificate hash file not found, which means that there is not
+ // SourceStamp present.
+ return SourceStampVerificationResult.notPresent();
+ }
+ return verify(apk, sourceStampCertificateDigest);
+ } catch (IOException e) {
+ // Any exception in reading the APK returns a non-present SourceStamp outcome
+ // without affecting the outcome of any of the other signature schemes.
+ return SourceStampVerificationResult.notPresent();
+ } finally {
+ closeApkJar(apkJar);
}
}
- private static SourceStampVerificationResult verify(RandomAccessFile apk)
- throws IOException, SignatureNotFoundException {
- byte[] sourceStampCertificateDigest = getSourceStampCertificateDigest(apk);
- if (sourceStampCertificateDigest == null) {
- // SourceStamp certificate hash file not found, which means that there is not
- // SourceStamp present.
- return SourceStampVerificationResult.notPresent();
+ private static SourceStampVerificationResult verify(
+ RandomAccessFile apk, byte[] sourceStampCertificateDigest) {
+ try {
+ SignatureInfo signatureInfo =
+ ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID);
+ Map<Integer, byte[]> apkContentDigests = getApkContentDigests(apk);
+ return verify(signatureInfo, apkContentDigests, sourceStampCertificateDigest);
+ } catch (IOException | SignatureNotFoundException e) {
+ return SourceStampVerificationResult.notVerified();
}
- SignatureInfo signatureInfo =
- ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID);
- Map<Integer, byte[]> apkContentDigests = getApkContentDigests(apk);
- return verify(signatureInfo, apkContentDigests, sourceStampCertificateDigest);
}
private static SourceStampVerificationResult verify(
@@ -255,22 +289,17 @@
return apkContentDigests;
}
- private static byte[] getSourceStampCertificateDigest(RandomAccessFile apk) throws IOException {
- StrictJarFile apkJar =
- new StrictJarFile(
- apk.getFD(),
- /* verify= */ false,
- /* signatureSchemeRollbackProtectionsEnforced= */ false);
- ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME);
- if (zipEntry == null) {
- // SourceStamp certificate hash file not found, which means that there is not
- // SourceStamp present.
- return null;
- }
+ private static byte[] getSourceStampCertificateDigest(StrictJarFile apkJar) throws IOException {
InputStream inputStream = null;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
+ ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME);
+ if (zipEntry == null) {
+ // SourceStamp certificate hash file not found, which means that there is not
+ // SourceStamp present.
+ return null;
+ }
inputStream = apkJar.getInputStream(zipEntry);
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// Trying to read the certificate digest, which should be less than 1024 bytes.
byte[] buffer = new byte[1024];
@@ -299,4 +328,15 @@
}
return result.array();
}
+
+ private static void closeApkJar(StrictJarFile apkJar) {
+ try {
+ if (apkJar == null) {
+ return;
+ }
+ apkJar.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not close APK jar", e);
+ }
+ }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0dcb9cc..dffcafe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1307,6 +1307,15 @@
}
/**
+ * Returns true if the display is in active state such as {@link #STATE_ON}
+ * or {@link #STATE_VR}.
+ * @hide
+ */
+ public static boolean isActiveState(int state) {
+ return state == STATE_ON || state == STATE_VR;
+ }
+
+ /**
* A mode supported by a given display.
*
* @see Display#getSupportedModes()
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 3927ebf..9f2d36d 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -102,9 +102,9 @@
void closeSystemDialogs(String reason);
/**
- * Called for wallpaper windows when their offsets change.
+ * Called for wallpaper windows when their offsets or zoom level change.
*/
- void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync);
+ void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom, boolean sync);
void dispatchWallpaperCommand(String action, int x, int y,
int z, in Bundle extras, boolean sync);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 91000a9..dfe89a3 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -221,6 +221,19 @@
*/
void setWallpaperPosition(IBinder windowToken, float x, float y, float xstep, float ystep);
+ /**
+ * For wallpaper windows, sets the scale of the wallpaper based on
+ * SystemUI behavior.
+ */
+ void setWallpaperZoomOut(IBinder windowToken, float scale);
+
+ /**
+ * For wallpaper windows, sets whether the wallpaper should actually be
+ * scaled when setWallpaperZoomOut is called. If set to false, the WallpaperService will
+ * receive the zoom out value but the surface won't be scaled.
+ */
+ void setShouldZoomOutWallpaper(IBinder windowToken, boolean shouldZoom);
+
@UnsupportedAppUsage
void wallpaperOffsetsComplete(IBinder window);
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 74fac2b..6784cf7 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -165,10 +165,16 @@
if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) {
return;
}
- if (mServedView == view || !view.hasImeFocus() || !view.hasWindowFocus()) {
+ if (!view.hasImeFocus() || !view.hasWindowFocus()) {
return;
}
- mNextServedView = hasFocus ? view : null;
+ if (DEBUG) Log.d(TAG, "onViewFocusChanged, view=" + view + ", mServedView=" + mServedView);
+
+ if (hasFocus) {
+ mNextServedView = view;
+ } else if (view == mServedView) {
+ mNextServedView = null;
+ }
mViewRootImpl.dispatchCheckFocus();
}
diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java
index 5d5edec..4227f78 100644
--- a/core/java/android/view/InsetsAnimationControlCallbacks.java
+++ b/core/java/android/view/InsetsAnimationControlCallbacks.java
@@ -16,7 +16,6 @@
package android.view;
-import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.WindowInsetsAnimation.Bounds;
/**
@@ -37,7 +36,7 @@
void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation,
- Bounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation);
+ Bounds bounds);
/**
* Schedule the apply by posting the animation callback.
@@ -46,10 +45,10 @@
/**
* Finish the final steps after the animation.
- * @param controller The controller used to control the animation.
+ * @param runner The runner used to run the animation.
* @param shown {@code true} if the insets are shown.
*/
- void notifyFinished(InsetsAnimationControlImpl controller, boolean shown);
+ void notifyFinished(InsetsAnimationControlRunner runner, boolean shown);
/**
* Apply the new params to the surface.
@@ -57,4 +56,10 @@
* apply.
*/
void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params);
+
+ /**
+ * Post a message to release the Surface, guaranteed to happen after all
+ * previous calls to applySurfaceParams.
+ */
+ void releaseSurfaceControlFromRt(SurfaceControl sc);
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index ae509f3..d2e6036 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -31,9 +31,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
-import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.InsetsState.InternalInsetsSide;
-import android.view.InsetsState.InternalInsetsType;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
@@ -49,7 +47,8 @@
* @hide
*/
@VisibleForTesting
-public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
+public class InsetsAnimationControlImpl implements WindowInsetsAnimationController,
+ InsetsAnimationControlRunner {
private final Rect mTmpFrame = new Rect();
@@ -84,8 +83,7 @@
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
- boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- @AnimationType int animationType) {
+ boolean fade, @AnimationType int animationType) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -105,7 +103,7 @@
mAnimation.setAlpha(getCurrentAlpha());
mAnimationType = animationType;
mController.startAnimation(this, listener, types, mAnimation,
- new Bounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation);
+ new Bounds(mHiddenInsets, mShownInsets));
}
@Override
@@ -133,11 +131,8 @@
return mTypes;
}
- boolean controlsInternalType(@InternalInsetsType int type) {
- return InsetsState.toInternalType(mTypes).contains(type);
- }
-
- @AnimationType int getAnimationType() {
+ @Override
+ public @AnimationType int getAnimationType() {
return mAnimationType;
}
@@ -185,10 +180,19 @@
mAnimation.setAlpha(mPendingAlpha);
if (mFinished) {
mController.notifyFinished(this, mShownOnFinish);
+ releaseLeashes();
}
return mFinished;
}
+ private void releaseLeashes() {
+ for (int i = mControls.size() - 1; i >= 0; i--) {
+ final InsetsSourceControl c = mControls.valueAt(i);
+ if (c == null) continue;
+ c.release(mController::releaseSurfaceControlFromRt);
+ }
+ }
+
@Override
public void finish(boolean shown) {
if (mCancelled || mFinished) {
@@ -196,6 +200,8 @@
}
setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */);
mFinished = true;
+ mListener.onFinished(this);
+
mShownOnFinish = shown;
}
@@ -205,19 +211,29 @@
return mAnimation.getFraction();
}
- public void onCancelled() {
+ @Override
+ public void cancel() {
if (mFinished) {
return;
}
mCancelled = true;
- mListener.onCancelled();
+ mListener.onCancelled(this);
+
+ releaseLeashes();
}
+ @Override
+ public boolean isFinished() {
+ return mFinished;
+ }
+
+ @Override
public boolean isCancelled() {
return mCancelled;
}
- WindowInsetsAnimation getAnimation() {
+ @Override
+ public WindowInsetsAnimation getAnimation() {
return mAnimation;
}
@@ -225,6 +241,10 @@
return mListener;
}
+ SparseArray<InsetsSourceControl> getControls() {
+ return mControls;
+ }
+
private Insets calculateInsets(InsetsState state, Rect frame,
SparseArray<InsetsSourceControl> controls, boolean shown,
@Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
new file mode 100644
index 0000000..0711c3e
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -0,0 +1,56 @@
+/*
+ * 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 android.view;
+
+import android.view.InsetsController.AnimationType;
+import android.view.InsetsState.InternalInsetsType;
+import android.view.WindowInsets.Type.InsetsType;
+
+/**
+ * Interface representing a runner for an insets animation.
+ *
+ * @hide
+ */
+public interface InsetsAnimationControlRunner {
+
+ /**
+ * @return The {@link InsetsType} the animation of this runner is controlling.
+ */
+ @InsetsType int getTypes();
+
+ /**
+ * Cancels the animation.
+ */
+ void cancel();
+
+ /**
+ * @return The animation this runner is running.
+ */
+ WindowInsetsAnimation getAnimation();
+
+ /**
+ * @return Whether {@link #getTypes()} maps to a specific {@link InternalInsetsType}.
+ */
+ default boolean controlsInternalType(@InternalInsetsType int type) {
+ return InsetsState.toInternalType(getTypes()).contains(type);
+ }
+
+ /**
+ * @return The animation type this runner is running.
+ */
+ @AnimationType int getAnimationType();
+}
diff --git a/core/java/android/view/InsetsAnimationThread.java b/core/java/android/view/InsetsAnimationThread.java
new file mode 100644
index 0000000..cdf9733
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationThread.java
@@ -0,0 +1,70 @@
+/*
+ * 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 android.view;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Trace;
+
+/**
+ * Thread to be used for inset animations to be running off the main thread.
+ * @hide
+ */
+public class InsetsAnimationThread extends HandlerThread {
+
+ private static InsetsAnimationThread sInstance;
+ private static Handler sHandler;
+
+ private InsetsAnimationThread() {
+ // TODO: Should this use higher priority?
+ super("InsetsAnimations");
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new InsetsAnimationThread();
+ sInstance.start();
+ sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_VIEW);
+ sHandler = new Handler(sInstance.getLooper());
+ }
+ }
+
+ public static void release() {
+ synchronized (InsetsAnimationThread.class) {
+ if (sInstance == null) {
+ return;
+ }
+ sInstance.getLooper().quitSafely();
+ sInstance = null;
+ sHandler = null;
+ }
+ }
+
+ public static InsetsAnimationThread get() {
+ synchronized (InsetsAnimationThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (InsetsAnimationThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
new file mode 100644
index 0000000..13b4cd8
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -0,0 +1,136 @@
+/*
+ * 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 android.view;
+
+import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
+
+import android.annotation.UiThread;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.util.SparseArray;
+import android.view.InsetsController.AnimationType;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsAnimation.Bounds;
+import android.view.animation.Interpolator;
+
+/**
+ * Insets animation runner that uses {@link InsetsAnimationThread} to run the animation off from the
+ * main thread.
+ *
+ * @hide
+ */
+public class InsetsAnimationThreadControlRunner implements InsetsAnimationControlRunner {
+
+ private final InsetsAnimationControlImpl mControl;
+ private final InsetsAnimationControlCallbacks mOuterCallbacks;
+ private final Handler mMainThreadHandler;
+ private final InsetsState mState = new InsetsState();
+ private final InsetsAnimationControlCallbacks mCallbacks =
+ new InsetsAnimationControlCallbacks() {
+
+ private final float[] mTmpFloat9 = new float[9];
+
+ @Override
+ @UiThread
+ public void startAnimation(InsetsAnimationControlImpl controller,
+ WindowInsetsAnimationControlListener listener, int types,
+ WindowInsetsAnimation animation, Bounds bounds) {
+ // Animation will be started in constructor already.
+ }
+
+ @Override
+ public void scheduleApplyChangeInsets() {
+ mControl.applyChangeInsets(mState);
+ }
+
+ @Override
+ public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
+ releaseControls(mControl.getControls());
+ mMainThreadHandler.post(() ->
+ mOuterCallbacks.notifyFinished(InsetsAnimationThreadControlRunner.this, shown));
+ }
+
+ @Override
+ public void applySurfaceParams(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();
+ t.close();
+ }
+
+ @Override
+ public void releaseSurfaceControlFromRt(SurfaceControl sc) {
+ // Since we don't push the SurfaceParams to the RT we can release directly
+ sc.release();
+ }
+ };
+
+ @UiThread
+ public InsetsAnimationThreadControlRunner(SparseArray<InsetsSourceControl> controls, Rect frame,
+ InsetsState state, WindowInsetsAnimationControlListener listener,
+ @InsetsType int types,
+ InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
+ boolean fade, @AnimationType int animationType, Handler mainThreadHandler) {
+ mMainThreadHandler = mainThreadHandler;
+ mOuterCallbacks = controller;
+ mControl = new InsetsAnimationControlImpl(copyControls(controls), frame, state, listener,
+ types, mCallbacks, durationMs, interpolator, fade, animationType);
+ InsetsAnimationThread.getHandler().post(() -> listener.onReady(mControl, types));
+ }
+
+ private void releaseControls(SparseArray<InsetsSourceControl> controls) {
+ for (int i = controls.size() - 1; i >= 0; i--) {
+ controls.valueAt(i).release(SurfaceControl::release);
+ }
+ }
+
+ private SparseArray<InsetsSourceControl> copyControls(
+ SparseArray<InsetsSourceControl> controls) {
+ SparseArray<InsetsSourceControl> copy = new SparseArray<>(controls.size());
+ for (int i = 0; i < controls.size(); i++) {
+ copy.append(controls.keyAt(i), new InsetsSourceControl(controls.valueAt(i)));
+ }
+ return copy;
+ }
+
+ @Override
+ @UiThread
+ public int getTypes() {
+ return mControl.getTypes();
+ }
+
+ @Override
+ @UiThread
+ public void cancel() {
+ InsetsAnimationThread.getHandler().post(mControl::cancel);
+ }
+
+ @Override
+ @UiThread
+ public WindowInsetsAnimation getAnimation() {
+ return mControl.getAnimation();
+ }
+
+ @Override
+ public int getAnimationType() {
+ return mControl.getAnimationType();
+ }
+}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 607886f..96f7340 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
+import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -52,7 +53,7 @@
import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -167,10 +168,22 @@
private WindowInsetsAnimationController mController;
private ObjectAnimator mAnimator;
- protected boolean mShow;
+ private final boolean mShow;
+ private final boolean mUseSfVsync;
- public InternalAnimationControlListener(boolean show) {
+ private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+ new ThreadLocal<AnimationHandler>() {
+ @Override
+ protected AnimationHandler initialValue() {
+ AnimationHandler handler = new AnimationHandler();
+ handler.setProvider(new SfVsyncFrameCallbackProvider());
+ return handler;
+ }
+ };
+
+ public InternalAnimationControlListener(boolean show, boolean useSfVsync) {
mShow = show;
+ mUseSfVsync = useSfVsync;
}
@Override
@@ -193,11 +206,18 @@
onAnimationFinish();
}
});
+ if (mUseSfVsync) {
+ mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
+ }
mAnimator.start();
}
@Override
- public void onCancelled() {
+ public void onFinished(WindowInsetsAnimationController controller) {
+ }
+
+ @Override
+ public void onCancelled(WindowInsetsAnimationController controller) {
// Animator can be null when it is cancelled before onReady() completes.
if (mAnimator != null) {
mAnimator.cancel();
@@ -228,12 +248,12 @@
*/
private static class RunningAnimation {
- RunningAnimation(InsetsAnimationControlImpl control, int type) {
- this.control = control;
+ RunningAnimation(InsetsAnimationControlRunner runner, int type) {
+ this.runner = runner;
this.type = type;
}
- final InsetsAnimationControlImpl control;
+ final InsetsAnimationControlRunner runner;
final @AnimationType int type;
/**
@@ -252,7 +272,7 @@
PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
long durationMs, Interpolator interpolator, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- CancellationSignal cancellationSignal) {
+ CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) {
this.types = types;
this.listener = listener;
this.durationMs = durationMs;
@@ -260,6 +280,7 @@
this.animationType = animationType;
this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
this.cancellationSignal = cancellationSignal;
+ this.useInsetsAnimationThread = useInsetsAnimationThread;
}
final @InsetsType int types;
@@ -269,6 +290,7 @@
final @AnimationType int animationType;
final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
final CancellationSignal cancellationSignal;
+ final boolean useInsetsAnimationThread;
}
private final String TAG = "InsetsControllerImpl";
@@ -347,15 +369,20 @@
InsetsState state = new InsetsState(mState, true /* copySources */);
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
- InsetsAnimationControlImpl control = runningAnimation.control;
+ InsetsAnimationControlRunner runner = runningAnimation.runner;
+ if (runner instanceof InsetsAnimationControlImpl) {
+ InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner;
- // Keep track of running animation to be dispatched. Aggregate it here such that if
- // it gets finished within applyChangeInsets we still dispatch it to onProgress.
- if (runningAnimation.startDispatched) {
- mTmpRunningAnims.add(control.getAnimation());
- }
- if (control.applyChangeInsets(state)) {
- mTmpFinishedControls.add(control);
+ // Keep track of running animation to be dispatched. Aggregate it here such that
+ // if it gets finished within applyChangeInsets we still dispatch it to
+ // onProgress.
+ if (runningAnimation.startDispatched) {
+ mTmpRunningAnims.add(control.getAnimation());
+ }
+
+ if (control.applyChangeInsets(state)) {
+ mTmpFinishedControls.add(control);
+ }
}
}
@@ -494,13 +521,13 @@
PendingControlRequest pendingRequest = mPendingImeControlRequest;
mPendingImeControlRequest = null;
mHandler.removeCallbacks(mPendingControlTimeout);
- CancellationSignal cancellationSignal = controlAnimationUnchecked(
- pendingRequest.types,
+ controlAnimationUnchecked(
+ pendingRequest.types, pendingRequest.cancellationSignal,
pendingRequest.listener, mFrame,
true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
false /* fade */, pendingRequest.animationType,
- pendingRequest.layoutInsetsDuringAnimation);
- pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);
+ pendingRequest.layoutInsetsDuringAnimation,
+ pendingRequest.useInsetsAnimationThread);
return;
}
@@ -546,24 +573,27 @@
}
@Override
- public CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
+ public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
@Nullable Interpolator interpolator,
+ @Nullable CancellationSignal cancellationSignal,
@NonNull WindowInsetsAnimationControlListener listener) {
- return controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
- interpolator, ANIMATION_TYPE_USER);
+ controlWindowInsetsAnimation(types, cancellationSignal, listener,
+ false /* fromIme */, durationMillis, interpolator, ANIMATION_TYPE_USER);
}
- private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
- WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
- @Nullable Interpolator interpolator, @AnimationType int animationType) {
+ private void controlWindowInsetsAnimation(@InsetsType int types,
+ @Nullable CancellationSignal cancellationSignal,
+ WindowInsetsAnimationControlListener listener,
+ boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
+ @AnimationType int animationType) {
if (!checkDisplayFramesForControlling()) {
- listener.onCancelled();
- CancellationSignal cancellationSignal = new CancellationSignal();
- cancellationSignal.cancel();
- return cancellationSignal;
+ listener.onCancelled(null);
+ return;
}
- return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
- false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
+ controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
+ interpolator, false /* fade */, animationType,
+ getLayoutInsetsDuringAnimationMode(types),
+ false /* useInsetsAnimationThread */);
}
private boolean checkDisplayFramesForControlling() {
@@ -573,17 +603,17 @@
return mState.getDisplayFrame().equals(mFrame);
}
- private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
+ private void controlAnimationUnchecked(@InsetsType int types,
+ @Nullable CancellationSignal cancellationSignal,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
long durationMs, Interpolator interpolator, boolean fade,
@AnimationType int animationType,
- @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
- CancellationSignal cancellationSignal = new CancellationSignal();
+ @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
+ boolean useInsetsAnimationThread) {
if (types == 0) {
// nothing to animate.
- listener.onCancelled();
- cancellationSignal.cancel();
- return cancellationSignal;
+ listener.onCancelled(null);
+ return;
}
cancelExistingControllers(types);
mLastStartedAnimTypes |= types;
@@ -600,29 +630,42 @@
abortPendingImeControlRequest();
final PendingControlRequest request = new PendingControlRequest(types,
listener, durationMs,
- interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal);
+ interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal,
+ useInsetsAnimationThread);
mPendingImeControlRequest = request;
mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
- cancellationSignal.setOnCancelListener(() -> {
- if (mPendingImeControlRequest == request) {
- abortPendingImeControlRequest();
- }
- });
- return cancellationSignal;
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(() -> {
+ if (mPendingImeControlRequest == request) {
+ abortPendingImeControlRequest();
+ }
+ });
+ }
+ return;
}
if (typesReady == 0) {
- listener.onCancelled();
- cancellationSignal.cancel();
- return cancellationSignal;
+ listener.onCancelled(null);
+ return;
}
- final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
- frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
- layoutInsetsDuringAnimation, animationType);
- mRunningAnimations.add(new RunningAnimation(controller, animationType));
- cancellationSignal.setOnCancelListener(controller::onCancelled);
- return cancellationSignal;
+
+ final InsetsAnimationControlRunner runner = useInsetsAnimationThread
+ ? new InsetsAnimationThreadControlRunner(controls,
+ frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
+ animationType, mViewRoot.mHandler)
+ : new InsetsAnimationControlImpl(controls,
+ frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
+ animationType);
+ mRunningAnimations.add(new RunningAnimation(runner, animationType));
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(runner::cancel);
+ }
+ if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
+ showDirectly(types);
+ } else {
+ hideDirectly(types, false /* animationFinished */, animationType);
+ }
}
/**
@@ -666,7 +709,7 @@
}
final InsetsSourceControl control = consumer.getControl();
if (control != null) {
- controls.put(consumer.getType(), control);
+ controls.put(consumer.getType(), new InsetsSourceControl(control));
typesReady |= toPublicType(consumer.getType());
} else if (animationType == ANIMATION_TYPE_SHOW) {
@@ -705,7 +748,7 @@
private void cancelExistingControllers(@InsetsType int types) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
- InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+ InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
if ((control.getTypes() & types) != 0) {
cancelAnimation(control, true /* invokeCallback */);
}
@@ -717,7 +760,7 @@
private void abortPendingImeControlRequest() {
if (mPendingImeControlRequest != null) {
- mPendingImeControlRequest.listener.onCancelled();
+ mPendingImeControlRequest.listener.onCancelled(null);
mPendingImeControlRequest = null;
mHandler.removeCallbacks(mPendingControlTimeout);
}
@@ -725,13 +768,13 @@
@VisibleForTesting
@Override
- public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
- cancelAnimation(controller, false /* invokeCallback */);
+ public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
+ cancelAnimation(runner, false /* invokeCallback */);
if (shown) {
- showDirectly(controller.getTypes());
+ showDirectly(runner.getTypes());
} else {
- hideDirectly(controller.getTypes(), true /* animationFinished */,
- controller.getAnimationType());
+ hideDirectly(runner.getTypes(), true /* animationFinished */,
+ runner.getAnimationType());
}
}
@@ -756,7 +799,7 @@
void notifyControlRevoked(InsetsSourceConsumer consumer) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
- InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+ InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
cancelAnimation(control, true /* invokeCallback */);
}
@@ -766,12 +809,12 @@
}
}
- private void cancelAnimation(InsetsAnimationControlImpl control, boolean invokeCallback) {
+ private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
if (invokeCallback) {
- control.onCancelled();
+ control.cancel();
}
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
- if (mRunningAnimations.get(i).control == control) {
+ if (mRunningAnimations.get(i).runner == control) {
mRunningAnimations.remove(i);
break;
}
@@ -844,7 +887,7 @@
@VisibleForTesting
public @AnimationType int getAnimationType(@InternalInsetsType int type) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
- InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+ InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
if (control.controlsInternalType(type)) {
return mRunningAnimations.get(i).type;
}
@@ -878,15 +921,25 @@
return;
}
+ boolean useInsetsAnimationThread = canUseInsetsAnimationThread();
final InternalAnimationControlListener listener =
- new InternalAnimationControlListener(show);
+ new InternalAnimationControlListener(show, useInsetsAnimationThread);
// Show/hide animations always need to be relative to the display frame, in order that shown
// and hidden state insets are correct.
controlAnimationUnchecked(
- types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
+ types, null /* cancellationSignal */, listener, mState.getDisplayFrame(), fromIme,
+ listener.getDurationMs(),
INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
- : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+ : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
+ useInsetsAnimationThread);
+ }
+
+ private boolean canUseInsetsAnimationThread() {
+ if (mViewRoot.mView == null) {
+ return true;
+ }
+ return !mViewRoot.mView.hasWindowInsetsAnimationCallback();
}
private void hideDirectly(
@@ -921,13 +974,7 @@
@Override
public void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types,
- WindowInsetsAnimation animation, Bounds bounds, int layoutDuringAnimation) {
- if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
- showDirectly(types);
- } else {
- hideDirectly(types, false /* animationFinished */, controller.getAnimationType());
- }
-
+ WindowInsetsAnimation animation, Bounds bounds) {
if (mViewRoot.mView == null) {
return;
}
@@ -941,7 +988,7 @@
}
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
- if (runningAnimation.control == controller) {
+ if (runningAnimation.runner == controller) {
runningAnimation.startDispatched = true;
}
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 252fc0c..3325734 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -117,7 +117,7 @@
}
}
if (lastControl != null) {
- lastControl.release(mController);
+ lastControl.release(mController::releaseSurfaceControlFromRt);
}
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 75f6eab..f3ec65f 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -22,6 +22,8 @@
import android.os.Parcelable;
import android.view.InsetsState.InternalInsetsType;
+import java.util.function.Consumer;
+
/**
* Represents a parcelable object to allow controlling a single {@link InsetsSource}.
* @hide
@@ -94,9 +96,9 @@
dest.writeParcelable(mSurfacePosition, 0 /* flags*/);
}
- public void release(InsetsController controller) {
+ public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
if (mLeash != null) {
- controller.releaseSurfaceControlFromRt(mLeash);
+ surfaceReleaseConsumer.accept(mLeash);
}
}
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 8ec5df8..18e0132 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -35,6 +35,7 @@
import com.android.internal.R;
import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.NotificationExpandButton;
import java.util.ArrayList;
@@ -56,7 +57,7 @@
private OnClickListener mAppOpsListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
private LinearLayout mTransferChip;
- private ImageView mExpandButton;
+ private NotificationExpandButton mExpandButton;
private CachingIconView mIcon;
private View mProfileBadge;
private View mOverlayIcon;
@@ -65,7 +66,6 @@
private View mAppOps;
private View mAudiblyAlertedIcon;
private int mIconColor;
- private int mOriginalNotificationColor;
private boolean mExpanded;
private boolean mShowExpandButtonAtEnd;
private boolean mShowWorkBadgeAtEnd;
@@ -324,13 +324,8 @@
return mIconColor;
}
- @RemotableViewMethod
- public void setOriginalNotificationColor(int color) {
- mOriginalNotificationColor = color;
- }
-
public int getOriginalNotificationColor() {
- return mOriginalNotificationColor;
+ return mExpandButton.getOriginalNotificationColor();
}
@RemotableViewMethod
@@ -371,7 +366,7 @@
contentDescriptionId = R.string.expand_button_content_description_collapsed;
}
mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
- mExpandButton.setColorFilter(mOriginalNotificationColor);
+ mExpandButton.setColorFilter(getOriginalNotificationColor());
mExpandButton.setContentDescription(mContext.getText(contentDescriptionId));
}
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 7f36418..229ee03 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -16,6 +16,8 @@
package android.view;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.CancellationSignal;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
@@ -60,21 +62,6 @@
}
@Override
- public CancellationSignal controlWindowInsetsAnimation(int types, long durationMillis,
- Interpolator interpolator,
- WindowInsetsAnimationControlListener listener) {
- if (mReplayedInsetsController != null) {
- return mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
- interpolator, listener);
- } else {
- listener.onCancelled();
- CancellationSignal cancellationSignal = new CancellationSignal();
- cancellationSignal.cancel();
- return cancellationSignal;
- }
- }
-
- @Override
public void setSystemBarsAppearance(int appearance, int mask) {
if (mReplayedInsetsController != null) {
mReplayedInsetsController.setSystemBarsAppearance(appearance, mask);
@@ -176,6 +163,19 @@
mReplayedInsetsController = null;
}
+ @Override
+ public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+ @Nullable Interpolator interpolator,
+ CancellationSignal cancellationSignal,
+ @NonNull WindowInsetsAnimationControlListener listener) {
+ if (mReplayedInsetsController != null) {
+ mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
+ interpolator, cancellationSignal, listener);
+ } else {
+ listener.onCancelled(null);
+ }
+ }
+
private interface PendingRequest {
void replay(InsetsController controller);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 25f5609..c87808b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1960,7 +1960,7 @@
* @hide
*/
public static ScreenshotGraphicBuffer captureLayersExcluding(SurfaceControl layer,
- Rect sourceCrop, float frameScale, SurfaceControl[] exclude) {
+ Rect sourceCrop, float frameScale, int format, SurfaceControl[] exclude) {
final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
long[] nativeExcludeObjects = new long[exclude.length];
for (int i = 0; i < exclude.length; i++) {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index a3b3f1f..b587fbe 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -26,6 +26,8 @@
import android.os.Parcelable;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
+import java.util.Objects;
+
/**
* Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy
* will render in to a root SurfaceControl, and receive input based on the SurfaceControl's
@@ -50,7 +52,7 @@
* a SurfaceView by calling {@link SurfaceView#setChildSurfacePackage}.
*/
public static final class SurfacePackage implements Parcelable {
- private final SurfaceControl mSurfaceControl;
+ private SurfaceControl mSurfaceControl;
private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) {
@@ -95,6 +97,16 @@
out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder());
}
+ /**
+ * Release the SurfaceControl associated with the SurfacePackage.
+ */
+ public void release() {
+ if (mSurfaceControl != null) {
+ mSurfaceControl.release();
+ }
+ mSurfaceControl = null;
+ }
+
public static final @NonNull Creator<SurfacePackage> CREATOR
= new Creator<SurfacePackage>() {
public SurfacePackage createFromParcel(Parcel in) {
@@ -159,7 +171,8 @@
* @hide
*/
@TestApi
- public void addView(@NonNull View view, WindowManager.LayoutParams attrs) {
+ public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) {
+ Objects.requireNonNull(view);
mViewRoot.setView(view, attrs, null);
}
@@ -172,11 +185,18 @@
* @param width The width to layout the View within, in pixels.
* @param height The height to layout the View within, in pixels.
*/
- public void addView(@NonNull View view, int width, int height) {
+ public void setView(@NonNull View view, int width, int height) {
final WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height,
WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
- addView(view, lp);
+ setView(view, lp);
+ }
+
+ /**
+ * @return The view passed to setView, or null if none has been passed.
+ */
+ public @Nullable View getView() {
+ return mViewRoot.getView();
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6236e6e..708a094 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3318,7 +3318,7 @@
* Flag indicating that the view is autofilled
*
* @see #isAutofilled()
- * @see #setAutofilled(boolean)
+ * @see #setAutofilled(boolean, boolean)
*/
private static final int PFLAG3_IS_AUTOFILLED = 0x10000;
@@ -3428,6 +3428,7 @@
* 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
* 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
* 1 PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
+ * 1 PFLAG4_AUTOFILL_HIDE_HIGHLIGHT
* |-------|-------|-------|-------|
*/
@@ -3470,6 +3471,11 @@
*/
static final int PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS = 0x000000100;
+ /**
+ * Flag indicating the field should not have yellow highlight when autofilled.
+ */
+ private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x100;
+
/* End of masks for mPrivateFlags4 */
/** @hide */
@@ -9170,6 +9176,13 @@
}
/**
+ * @hide
+ */
+ public boolean hideAutofillHighlight() {
+ return (mPrivateFlags4 & PFLAG4_AUTOFILL_HIDE_HIGHLIGHT) != 0;
+ }
+
+ /**
* Gets the {@link View}'s current autofill value.
*
* <p>By default returns {@code null}, but subclasses should override it and return an
@@ -11227,6 +11240,15 @@
}
/**
+ * @return {@code true} if any {@link WindowInsetsAnimation.Callback} is registered on the view
+ * or view tree of the sub-hierarchy {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasWindowInsetsAnimationCallback() {
+ return getListenerInfo().mWindowInsetsAnimationCallback != null;
+ }
+
+ /**
* Dispatches {@link WindowInsetsAnimation.Callback#onPrepare(WindowInsetsAnimation)}
* when Window Insets animation is being prepared.
* @param animation current animation
@@ -11741,7 +11763,7 @@
* @hide
*/
@TestApi
- public void setAutofilled(boolean isAutofilled) {
+ public void setAutofilled(boolean isAutofilled, boolean hideHighlight) {
boolean wasChanged = isAutofilled != isAutofilled();
if (wasChanged) {
@@ -11751,6 +11773,12 @@
mPrivateFlags3 &= ~PFLAG3_IS_AUTOFILLED;
}
+ if (hideHighlight) {
+ mPrivateFlags4 |= PFLAG4_AUTOFILL_HIDE_HIGHLIGHT;
+ } else {
+ mPrivateFlags4 &= ~PFLAG4_AUTOFILL_HIDE_HIGHLIGHT;
+ }
+
invalidate();
}
}
@@ -20569,6 +20597,7 @@
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
state.mIsAutofilled = isAutofilled();
+ state.mHideHighlight = hideAutofillHighlight();
state.mAutofillViewId = mAutofillViewId;
return state;
}
@@ -20645,7 +20674,7 @@
mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved;
}
if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
- setAutofilled(baseState.mIsAutofilled);
+ setAutofilled(baseState.mIsAutofilled, baseState.mHideHighlight);
}
if ((baseState.mSavedData & BaseSavedState.AUTOFILL_ID) != 0) {
// It can happen that views have the same view id and the restoration path will not
@@ -24078,12 +24107,13 @@
}
/**
- * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled.
+ * Draw {@link View#isAutofilled()} highlight over view if the view is autofilled, unless
+ * {@link #PFLAG4_AUTOFILL_HIDE_HIGHLIGHT} is enabled.
*
* @param canvas The canvas to draw on
*/
private void drawAutofilledHighlight(@NonNull Canvas canvas) {
- if (isAutofilled()) {
+ if (isAutofilled() && !hideAutofillHighlight()) {
Drawable autofilledHighlight = getAutofilledDrawable();
if (autofilledHighlight != null) {
@@ -28526,6 +28556,7 @@
int mSavedData;
String mStartActivityRequestWhoSaved;
boolean mIsAutofilled;
+ boolean mHideHighlight;
int mAutofillViewId;
/**
@@ -28549,6 +28580,7 @@
mSavedData = source.readInt();
mStartActivityRequestWhoSaved = source.readString();
mIsAutofilled = source.readBoolean();
+ mHideHighlight = source.readBoolean();
mAutofillViewId = source.readInt();
}
@@ -28568,6 +28600,7 @@
out.writeInt(mSavedData);
out.writeString(mStartActivityRequestWhoSaved);
out.writeBoolean(mIsAutofilled);
+ out.writeBoolean(mHideHighlight);
out.writeInt(mAutofillViewId);
}
@@ -29066,8 +29099,33 @@
mTreeObserver = new ViewTreeObserver(context);
}
+ @Nullable
+ ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
+ if (mContentCaptureManager != null) {
+ return mContentCaptureManager;
+ }
+ mContentCaptureManager = context.getSystemService(ContentCaptureManager.class);
+ return mContentCaptureManager;
+ }
+
+ void delayNotifyContentCaptureInsetsEvent(@NonNull Insets insets) {
+ if (mContentCaptureManager == null) {
+ return;
+ }
+
+ ArrayList<Object> events = ensureEvents(
+ mContentCaptureManager.getMainContentCaptureSession());
+ events.add(insets);
+ }
+
private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session,
@NonNull View view, boolean appeared) {
+ ArrayList<Object> events = ensureEvents(session);
+ events.add(appeared ? view : view.getAutofillId());
+ }
+
+ @NonNull
+ private ArrayList<Object> ensureEvents(@NonNull ContentCaptureSession session) {
if (mContentCaptureEvents == null) {
// Most of the time there will be just one session, so intial capacity is 1
mContentCaptureEvents = new SparseArray<>(1);
@@ -29079,16 +29137,8 @@
events = new ArrayList<>();
mContentCaptureEvents.put(sessionId, events);
}
- events.add(appeared ? view : view.getAutofillId());
- }
- @Nullable
- ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
- if (mContentCaptureManager != null) {
- return mContentCaptureManager;
- }
- mContentCaptureManager = context.getSystemService(ContentCaptureManager.class);
- return mContentCaptureManager;
+ return events;
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7d4ec3d..e34e84c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7258,6 +7258,34 @@
: DISPATCH_MODE_CONTINUE_ON_SUBTREE;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean hasWindowInsetsAnimationCallback() {
+ if (super.hasWindowInsetsAnimationCallback()) {
+ return true;
+ }
+
+ // If we are root-level content view that fits insets, we imitate consuming behavior, so
+ // no child will retrieve window insets animation callback.
+ // See dispatchWindowInsetsAnimationPrepare.
+ boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
+ || isFrameworkOptionalFitsSystemWindows();
+ if (isOptionalFitSystemWindows && mAttachInfo != null
+ && mAttachInfo.mContentOnApplyWindowInsetsListener != null) {
+ return false;
+ }
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ if (getChildAt(i).hasWindowInsetsAnimationCallback()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void dispatchWindowInsetsAnimationPrepare(
@NonNull WindowInsetsAnimation animation) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9e5298b..dd34bcb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -81,6 +81,7 @@
import android.graphics.Color;
import android.graphics.FrameInfo;
import android.graphics.HardwareRenderer.FrameDrawingCallback;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -2254,6 +2255,7 @@
insets = insets.consumeDisplayCutout();
}
host.dispatchApplyWindowInsets(insets);
+ mAttachInfo.delayNotifyContentCaptureInsetsEvent(insets.getInsets(Type.all()));
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
@@ -3118,6 +3120,8 @@
ViewStructure structure = session.newViewStructure(view);
view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
session.notifyViewAppeared(structure);
+ } else if (event instanceof Insets) {
+ mainSession.notifyViewInsetsChanged(sessionId, (Insets) event);
} else {
Log.w(mTag, "invalid content capture event: " + event);
}
@@ -9054,7 +9058,7 @@
@Override
public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
- boolean sync) {
+ float zoom, boolean sync) {
if (sync) {
try {
mWindowSession.wallpaperOffsetsComplete(asBinder());
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index e05c374..56b4951 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -131,6 +131,31 @@
}
/**
+ * Set the windowing mode of children of a given root task, without changing
+ * the windowing mode of the Task itself. This can be used during transitions
+ * for example to make the activity render it's fullscreen configuration
+ * while the Task is still in PIP, so you can complete the animation.
+ *
+ * TODO(b/134365562): Can be removed once TaskOrg drives full-screen
+ */
+ public WindowContainerTransaction setActivityWindowingMode(IWindowContainer container,
+ int windowingMode) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mActivityWindowingMode = windowingMode;
+ return this;
+ }
+
+ /**
+ * Sets the windowing mode of the given container.
+ */
+ public WindowContainerTransaction setWindowingMode(IWindowContainer container,
+ int windowingMode) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mWindowingMode = windowingMode;
+ return this;
+ }
+
+ /**
* Sets whether a container or any of its children can be focusable. When {@code false}, no
* child can be focused; however, when {@code true}, it is still possible for children to be
* non-focusable due to WM policy.
@@ -143,6 +168,18 @@
}
/**
+ * Sets whether a container or its children should be hidden. When {@code false}, the existing
+ * visibility of the container applies, but when {@code true} the container will be forced
+ * to be hidden.
+ */
+ public WindowContainerTransaction setHidden(IWindowContainer container, boolean hidden) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mHidden = hidden;
+ chg.mChangeMask |= Change.CHANGE_HIDDEN;
+ return this;
+ }
+
+ /**
* Set the smallestScreenWidth of a container.
*/
public WindowContainerTransaction setSmallestScreenWidthDp(IWindowContainer container,
@@ -225,9 +262,11 @@
public static final int CHANGE_FOCUSABLE = 1;
public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
public static final int CHANGE_PIP_CALLBACK = 1 << 2;
+ public static final int CHANGE_HIDDEN = 1 << 3;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
+ private boolean mHidden = false;
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
@@ -235,11 +274,15 @@
private Rect mPinnedBounds = null;
private SurfaceControl.Transaction mBoundsChangeTransaction = null;
+ private int mActivityWindowingMode = -1;
+ private int mWindowingMode = -1;
+
public Change() {}
protected Change(Parcel in) {
mConfiguration.readFromParcel(in);
mFocusable = in.readBoolean();
+ mHidden = in.readBoolean();
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
@@ -251,13 +294,24 @@
mBoundsChangeTransaction =
SurfaceControl.Transaction.CREATOR.createFromParcel(in);
}
+
+ mWindowingMode = in.readInt();
+ mActivityWindowingMode = in.readInt();
+ }
+
+ public int getWindowingMode() {
+ return mWindowingMode;
+ }
+
+ public int getActivityWindowingMode() {
+ return mActivityWindowingMode;
}
public Configuration getConfiguration() {
return mConfiguration;
}
- /** Gets the requested focusable value */
+ /** Gets the requested focusable state */
public boolean getFocusable() {
if ((mChangeMask & CHANGE_FOCUSABLE) == 0) {
throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first");
@@ -265,6 +319,14 @@
return mFocusable;
}
+ /** Gets the requested hidden state */
+ public boolean getHidden() {
+ if ((mChangeMask & CHANGE_HIDDEN) == 0) {
+ throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first");
+ }
+ return mHidden;
+ }
+
public int getChangeMask() {
return mChangeMask;
}
@@ -330,6 +392,7 @@
public void writeToParcel(Parcel dest, int flags) {
mConfiguration.writeToParcel(dest, flags);
dest.writeBoolean(mFocusable);
+ dest.writeBoolean(mHidden);
dest.writeInt(mChangeMask);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
@@ -340,6 +403,9 @@
if (mBoundsChangeTransaction != null) {
mBoundsChangeTransaction.writeToParcel(dest, flags);
}
+
+ dest.writeInt(mWindowingMode);
+ dest.writeInt(mActivityWindowingMode);
}
@Override
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index 701bd31..b61fa36 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -16,23 +16,32 @@
package android.view;
-import android.annotation.Hide;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.view.WindowInsets.Type.InsetsType;
import android.view.inputmethod.EditorInfo;
/**
- * Interface that informs the client about {@link WindowInsetsAnimationController} state changes.
+ * Listener that encapsulates a request to
+ * {@link WindowInsetsController#controlWindowInsetsAnimation}.
+ *
+ * <p>
+ * Insets can be controlled with the supplied {@link WindowInsetsAnimationController} from
+ * {@link #onReady} until either {@link #onFinished} or {@link #onCancelled}.
+ *
+ * <p>
+ * Once the control over insets is finished or cancelled, it will not be regained until a new
+ * request to {@link WindowInsetsController#controlWindowInsetsAnimation} is made.
+ *
+ * <p>
+ * The request to control insets can fail immediately. In that case {@link #onCancelled} will be
+ * invoked without a preceding {@link #onReady}.
+ *
+ * @see WindowInsetsController#controlWindowInsetsAnimation
*/
public interface WindowInsetsAnimationControlListener {
/**
- * @hide
- */
- default void onPrepare(int types) {
- }
-
- /**
* Called when the animation is ready to be controlled. This may be delayed when the IME needs
* to redraw because of an {@link EditorInfo} change, or when the window is starting up.
*
@@ -42,14 +51,40 @@
* {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
* wasn't able to gain the controls because it wasn't the IME target or not
* currently the window that's controlling the system bars.
+ * @see WindowInsetsAnimationController#isReady
*/
void onReady(@NonNull WindowInsetsAnimationController controller, @InsetsType int types);
/**
- * Called when the window no longer has control over the requested types. If it loses control
- * over one type, the whole control will be cancelled. If none of the requested types were
- * available when requesting the control, the animation control will be cancelled immediately
- * without {@link #onReady} being called.
+ * Called when the request for control over the insets has
+ * {@link WindowInsetsAnimationController#finish finished}.
+ *
+ * Once this callback is invoked, the supplied {@link WindowInsetsAnimationController}
+ * is no longer {@link WindowInsetsAnimationController#isReady() ready}.
+ *
+ * Control will not be regained until a new request
+ * to {@link WindowInsetsController#controlWindowInsetsAnimation} is made.
+ *
+ * @param controller the controller which has finished.
+ * @see WindowInsetsAnimationController#isFinished
*/
- void onCancelled();
+ void onFinished(@NonNull WindowInsetsAnimationController controller);
+
+ /**
+ * Called when the request for control over the insets has been cancelled, either
+ * because the {@link android.os.CancellationSignal} associated with the
+ * {@link WindowInsetsController#controlWindowInsetsAnimation request} has been invoked, or
+ * the window has lost control over the insets (e.g. because it lost focus).
+ *
+ * Once this callback is invoked, the supplied {@link WindowInsetsAnimationController}
+ * is no longer {@link WindowInsetsAnimationController#isReady() ready}.
+ *
+ * Control will not be regained until a new request
+ * to {@link WindowInsetsController#controlWindowInsetsAnimation} is made.
+ *
+ * @param controller the controller which has been cancelled, or null if the request
+ * was cancelled before {@link #onReady} was invoked.
+ * @see WindowInsetsAnimationController#isCancelled
+ */
+ void onCancelled(@Nullable WindowInsetsAnimationController controller);
}
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 2c7880b..c191a54 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -139,10 +139,43 @@
@FloatRange(from = 0f, to = 1f) float fraction);
/**
- * Finishes the animation, and leaves the windows shown or hidden. After invoking
- * {@link #finish(boolean)}, this instance is no longer valid.
+ * Finishes the animation, and leaves the windows shown or hidden.
+ * <p>
+ * After invoking {@link #finish(boolean)}, this instance is no longer {@link #isReady ready}.
+ *
* @param shown if {@code true}, the windows will be shown after finishing the
* animation. Otherwise they will be hidden.
*/
void finish(boolean shown);
+
+ /**
+ * Returns whether this instance is ready to be used to control window insets.
+ * <p>
+ * Instances are ready when passed in {@link WindowInsetsAnimationControlListener#onReady}
+ * and stop being ready when it is either {@link #isFinished() finished} or
+ * {@link #isCancelled() cancelled}.
+ *
+ * @return {@code true} if the instance is ready, {@code false} otherwise.
+ */
+ default boolean isReady() {
+ return !isFinished() && !isCancelled();
+ }
+
+ /**
+ * Returns whether this instance has been finished by a call to {@link #finish}.
+ *
+ * @see WindowInsetsAnimationControlListener#onFinished
+ * @return {@code true} if the instance is finished, {@code false} otherwise.
+ */
+ boolean isFinished();
+
+ /**
+ * Returns whether this instance has been cancelled by the system, or by invoking the
+ * {@link android.os.CancellationSignal} passed into
+ * {@link WindowInsetsController#controlWindowInsetsAnimation}.
+ *
+ * @see WindowInsetsAnimationControlListener#onCancelled
+ * @return {@code true} if the instance is cancelled, {@code false} otherwise.
+ */
+ boolean isCancelled();
}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 2ad557e..0282eca 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -156,16 +156,17 @@
* calculate {@link WindowInsetsAnimation#getInterpolatedFraction()}.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* windows are ready to be controlled, among other callbacks.
- * @return A cancellation signal that the caller can use to cancel the request to obtain
- * control, or once they have control, to cancel the control.
+ * @param cancellationSignal A cancellation signal that the caller can use to cancel the
+ * request to obtain control, or once they have control, to cancel the
+ * control.
* @see WindowInsetsAnimation#getFraction()
* @see WindowInsetsAnimation#getInterpolatedFraction()
* @see WindowInsetsAnimation#getInterpolator()
* @see WindowInsetsAnimation#getDurationMillis()
*/
- @NonNull
- CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+ void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
@Nullable Interpolator interpolator,
+ @Nullable CancellationSignal cancellationSignal,
@NonNull WindowInsetsAnimationControlListener listener);
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 867b648..77ce5c1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -89,6 +89,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -805,6 +806,7 @@
@ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY,
to = "APPLICATION_OVERLAY")
})
+ @WindowType
public int type;
/**
@@ -1244,13 +1246,47 @@
public static final int INVALID_WINDOW_TYPE = -1;
/**
+ * @hide
+ */
+ @IntDef(prefix = "TYPE_", value = {
+ TYPE_ACCESSIBILITY_OVERLAY,
+ TYPE_APPLICATION,
+ TYPE_APPLICATION_ATTACHED_DIALOG,
+ TYPE_APPLICATION_MEDIA,
+ TYPE_APPLICATION_OVERLAY,
+ TYPE_APPLICATION_PANEL,
+ TYPE_APPLICATION_STARTING,
+ TYPE_APPLICATION_SUB_PANEL,
+ TYPE_BASE_APPLICATION,
+ TYPE_DRAWN_APPLICATION,
+ TYPE_INPUT_METHOD,
+ TYPE_INPUT_METHOD_DIALOG,
+ TYPE_KEYGUARD,
+ TYPE_KEYGUARD_DIALOG,
+ TYPE_PHONE,
+ TYPE_PRIORITY_PHONE,
+ TYPE_PRIVATE_PRESENTATION,
+ TYPE_SEARCH_BAR,
+ TYPE_STATUS_BAR,
+ TYPE_STATUS_BAR_PANEL,
+ TYPE_SYSTEM_ALERT,
+ TYPE_SYSTEM_DIALOG,
+ TYPE_SYSTEM_ERROR,
+ TYPE_SYSTEM_OVERLAY,
+ TYPE_TOAST,
+ TYPE_WALLPAPER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WindowType {}
+
+ /**
* Return true if the window type is an alert window.
*
* @param type The window type.
* @return If the window type is an alert window.
* @hide
*/
- public static boolean isSystemAlertWindowType(int type) {
+ public static boolean isSystemAlertWindowType(@WindowType int type) {
switch (type) {
case TYPE_PHONE:
case TYPE_PRIORITY_PHONE:
@@ -2861,6 +2897,18 @@
private boolean mFitInsetsIgnoringVisibility = false;
/**
+ * {@link InsetsState.InternalInsetsType}s to be applied to the window
+ * If {@link #type} has the predefined insets (like {@link #TYPE_STATUS_BAR} or
+ * {@link #TYPE_NAVIGATION_BAR}), this field will be ignored.
+ *
+ * <p>Note: provide only one inset corresponding to the window type (like
+ * {@link InsetsState.InternalInsetsType#ITYPE_STATUS_BAR} or
+ * {@link InsetsState.InternalInsetsType#ITYPE_NAVIGATION_BAR})</p>
+ * @hide
+ */
+ public @InsetsState.InternalInsetsType int[] providesInsetsTypes;
+
+ /**
* Specifies types of insets that this window should avoid overlapping during layout.
*
* @param types which types of insets that this window should avoid. The initial value of
@@ -3081,6 +3129,12 @@
out.writeInt(mFitInsetsSides);
out.writeBoolean(mFitInsetsIgnoringVisibility);
out.writeBoolean(preferMinimalPostProcessing);
+ if (providesInsetsTypes != null) {
+ out.writeInt(providesInsetsTypes.length);
+ out.writeIntArray(providesInsetsTypes);
+ } else {
+ out.writeInt(0);
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -3142,6 +3196,11 @@
mFitInsetsSides = in.readInt();
mFitInsetsIgnoringVisibility = in.readBoolean();
preferMinimalPostProcessing = in.readBoolean();
+ int insetsTypesLength = in.readInt();
+ if (insetsTypesLength > 0) {
+ providesInsetsTypes = new int[insetsTypesLength];
+ in.readIntArray(providesInsetsTypes);
+ }
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -3402,6 +3461,11 @@
changes |= LAYOUT_CHANGED;
}
+ if (!Arrays.equals(providesInsetsTypes, o.providesInsetsTypes)) {
+ providesInsetsTypes = o.providesInsetsTypes;
+ changes |= LAYOUT_CHANGED;
+ }
+
return changes;
}
@@ -3574,6 +3638,14 @@
sb.append(System.lineSeparator());
sb.append(prefix).append(" fitIgnoreVis");
}
+ if (providesInsetsTypes != null) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" insetsTypes=");
+ for (int i = 0; i < providesInsetsTypes.length; ++i) {
+ if (i > 0) sb.append(' ');
+ sb.append(InsetsState.typeToString(providesInsetsTypes[i]));
+ }
+ }
sb.append('}');
return sb.toString();
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 01a1c77..410d9af 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -504,6 +504,7 @@
}
void doRemoveView(ViewRootImpl root) {
+ boolean allViewsRemoved;
synchronized (mLock) {
final int index = mRoots.indexOf(root);
if (index >= 0) {
@@ -512,10 +513,17 @@
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
+ allViewsRemoved = mRoots.isEmpty();
}
if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
doTrimForeground();
}
+
+ // If we don't have any views anymore in our process, we no longer need the
+ // InsetsAnimationThread to save some resources.
+ if (allViewsRemoved) {
+ InsetsAnimationThread.release();
+ }
}
private int findViewLocked(View view, boolean required) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 87dcba0..144f8e3 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -312,6 +312,14 @@
}
@Override
+ public void setWallpaperZoomOut(android.os.IBinder windowToken, float zoom) {
+ }
+
+ @Override
+ public void setShouldZoomOutWallpaper(android.os.IBinder windowToken, boolean shouldZoom) {
+ }
+
+ @Override
public void wallpaperOffsetsComplete(android.os.IBinder window) {
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index ce7cfa7..39a9ed4 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -17,6 +17,7 @@
package android.view.autofill;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;
@@ -63,6 +64,7 @@
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
import android.widget.EditText;
+import android.widget.TextView;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -983,6 +985,10 @@
if (!isClientDisablingEnterExitEvent()) {
final AutofillValue value = view.getAutofillValue();
+ if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
+ flags |= FLAG_PASSWORD_INPUT_TYPE;
+ }
+
if (!isActiveLocked()) {
// Starts new session.
startSessionLocked(id, null, value, flags);
@@ -1149,6 +1155,10 @@
} else {
// don't notify entered when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
+ if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) {
+ flags |= FLAG_PASSWORD_INPUT_TYPE;
+ }
+
if (!isActiveLocked()) {
// Starts new session.
startSessionLocked(id, bounds, null, flags);
@@ -1226,7 +1236,7 @@
// If the session is gone some fields might still be highlighted, hence we have to
// remove the isAutofilled property even if no sessions are active.
if (mLastAutofilledData == null) {
- view.setAutofilled(false);
+ view.setAutofilled(false, false);
} else {
id = view.getAutofillId();
if (mLastAutofilledData.containsKey(id)) {
@@ -1234,13 +1244,13 @@
valueWasRead = true;
if (Objects.equals(mLastAutofilledData.get(id), value)) {
- view.setAutofilled(true);
+ view.setAutofilled(true, false);
} else {
- view.setAutofilled(false);
+ view.setAutofilled(false, false);
mLastAutofilledData.remove(id);
}
} else {
- view.setAutofilled(false);
+ view.setAutofilled(false, false);
}
}
@@ -2156,7 +2166,8 @@
* @param view The view that is to be autofilled
* @param targetValue The value we want to fill into view
*/
- private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
+ private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue,
+ boolean hideHighlight) {
AutofillValue currentValue = view.getAutofillValue();
if (Objects.equals(currentValue, targetValue)) {
synchronized (mLock) {
@@ -2165,11 +2176,12 @@
}
mLastAutofilledData.put(view.getAutofillId(), targetValue);
}
- view.setAutofilled(true);
+ view.setAutofilled(true, hideHighlight);
}
}
- private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+ private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
+ boolean hideHighlight) {
synchronized (mLock) {
if (sessionId != mSessionId) {
return;
@@ -2228,7 +2240,7 @@
// synchronously.
// If autofill happens async, the view is set to autofilled in
// notifyValueChanged.
- setAutofilledIfValuesIs(view, value);
+ setAutofilledIfValuesIs(view, value, hideHighlight);
numApplied++;
}
@@ -3246,10 +3258,11 @@
}
@Override
- public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+ public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
+ boolean hideHighlight) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.autofill(sessionId, ids, values));
+ afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
}
}
@@ -3387,10 +3400,11 @@
}
@Override
- public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+ public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
+ boolean hideHighlight) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.autofill(sessionId, ids, values));
+ afm.post(() -> afm.autofill(sessionId, ids, values, hideHighlight));
}
}
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 03054df..8526c1e 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -38,7 +38,8 @@
/**
* Autofills the activity with the contents of the values.
*/
- void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+ void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values,
+ boolean hideHighlight);
/**
* Requests showing the fill UI.
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 3903665..4371b3c 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -44,7 +44,8 @@
/**
* Autofills the activity with the contents of a dataset.
*/
- void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+ void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values,
+ boolean hideHighlight);
/**
* Authenticates a fill response or a data set.
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 7487ec4..44b4353 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Insets;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
@@ -84,6 +85,11 @@
}
@Override
+ void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
+ getMainCaptureSession().notifyViewInsetsChanged(mId, viewInsets);
+ }
+
+ @Override
public void internalNotifyViewTreeEvent(boolean started) {
getMainCaptureSession().notifyViewTreeEvent(mId, started);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index c29d251..ea34d94 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.graphics.Insets;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -112,6 +113,11 @@
*/
public static final int TYPE_SESSION_PAUSED = 8;
+ /**
+ * Called when the view's insets are changed. The new insets associated with the
+ * event may then be retrieved by calling {@link #getInsets()}
+ */
+ public static final int TYPE_VIEW_INSETS_CHANGED = 9;
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
@@ -122,7 +128,8 @@
TYPE_VIEW_TREE_APPEARED,
TYPE_CONTEXT_UPDATED,
TYPE_SESSION_PAUSED,
- TYPE_SESSION_RESUMED
+ TYPE_SESSION_RESUMED,
+ TYPE_VIEW_INSETS_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType{}
@@ -136,6 +143,7 @@
private @Nullable CharSequence mText;
private int mParentSessionId = NO_SESSION_ID;
private @Nullable ContentCaptureContext mClientContext;
+ private @Nullable Insets mInsets;
/** @hide */
public ContentCaptureEvent(int sessionId, int type, long eventTime) {
@@ -242,6 +250,13 @@
return this;
}
+ /** @hide */
+ @NonNull
+ public ContentCaptureEvent setInsets(@NonNull Insets insets) {
+ mInsets = insets;
+ return this;
+ }
+
/**
* Gets the type of the event.
*
@@ -305,6 +320,16 @@
}
/**
+ * Gets the rectangle of the insets associated with the event. Valid insets will only be
+ * returned if the type of the event is {@link #TYPE_VIEW_INSETS_CHANGED}, otherwise they
+ * will be null.
+ */
+ @Nullable
+ public Insets getInsets() {
+ return mInsets;
+ }
+
+ /**
* Merges event of the same type, either {@link #TYPE_VIEW_TEXT_CHANGED}
* or {@link #TYPE_VIEW_DISAPPEARED}.
*
@@ -369,7 +394,9 @@
}
if (mClientContext != null) {
pw.print(", context="); mClientContext.dump(pw); pw.println();
-
+ }
+ if (mInsets != null) {
+ pw.print(", insets="); pw.println(mInsets);
}
}
@@ -401,6 +428,9 @@
if (mClientContext != null) {
string.append(", context=").append(mClientContext);
}
+ if (mInsets != null) {
+ string.append(", insets=").append(mInsets);
+ }
return string.append(']').toString();
}
@@ -424,6 +454,9 @@
if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) {
parcel.writeParcelable(mClientContext, flags);
}
+ if (mType == TYPE_VIEW_INSETS_CHANGED) {
+ parcel.writeParcelable(mInsets, flags);
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -455,6 +488,9 @@
if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) {
event.setClientContext(parcel.readParcelable(null));
}
+ if (type == TYPE_VIEW_INSETS_CHANGED) {
+ event.setInsets(parcel.readParcelable(null));
+ }
return event;
}
@@ -488,6 +524,8 @@
return "VIEW_TREE_APPEARED";
case TYPE_CONTEXT_UPDATED:
return "CONTEXT_UPDATED";
+ case TYPE_VIEW_INSETS_CHANGED:
+ return "VIEW_INSETS_CHANGED";
default:
return "UKNOWN_TYPE: " + type;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 2134dab..012f5e6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.graphics.Insets;
import android.util.DebugUtils;
import android.util.Log;
import android.view.View;
@@ -440,6 +441,19 @@
abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
@Nullable CharSequence text);
+ /**
+ * Notifies the Intelligence Service that the insets of a view have changed.
+ */
+ public final void notifyViewInsetsChanged(@NonNull Insets viewInsets) {
+ Preconditions.checkNotNull(viewInsets);
+
+ if (!isContentCaptureEnabled()) return;
+
+ internalNotifyViewInsetsChanged(viewInsets);
+ }
+
+ abstract void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets);
+
/** @hide */
public abstract void internalNotifyViewTreeEvent(boolean started);
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 96f224f..893d38d 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -22,6 +22,7 @@
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_INSETS_CHANGED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
@@ -36,6 +37,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ParceledListSlice;
+import android.graphics.Insets;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -578,6 +580,11 @@
}
@Override
+ void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
+ notifyViewInsetsChanged(mId, viewInsets);
+ }
+
+ @Override
public void internalNotifyViewTreeEvent(boolean started) {
notifyViewTreeEvent(mId, started);
}
@@ -642,6 +649,12 @@
}
/** Public because is also used by ViewRootImpl */
+ public void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
+ .setInsets(viewInsets));
+ }
+
+ /** Public because is also used by ViewRootImpl */
public void notifyViewTreeEvent(int sessionId, boolean started) {
final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index 3cc04b8..3788e2b 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -58,7 +58,7 @@
- // Code below generated by codegen v1.0.14.
+ // Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -104,8 +104,8 @@
}
/**
- * The extras encoding the UI style information. Defaults to null in which case the default
- * system UI style will be used.
+ * The extras encoding the UI style information. Defaults to {@code null} in which case the
+ * default system UI style will be used.
*/
@DataClass.Generated.Member
public @Nullable Bundle getStyle() {
@@ -244,11 +244,11 @@
}
/**
- * The extras encoding the UI style information. Defaults to null in which case the default
- * system UI style will be used.
+ * The extras encoding the UI style information. Defaults to {@code null} in which case the
+ * default system UI style will be used.
*/
@DataClass.Generated.Member
- public @NonNull Builder setStyle(@Nullable Bundle value) {
+ public @NonNull Builder setStyle(@NonNull Bundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
mStyle = value;
@@ -279,8 +279,8 @@
}
@DataClass.Generated(
- time = 1582078731418L,
- codegenVersion = "1.0.14",
+ time = 1584067238741L,
+ codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable android.os.Bundle mStyle\nprivate static android.os.Bundle defaultStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index 6500613..dd1738a 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.BinderThread;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -94,19 +95,21 @@
}
-
/**
* Inflates a view with the content of this suggestion at a specific size.
* The size must be between the {@link InlinePresentationSpec#getMinSize() min size}
* and the {@link InlinePresentationSpec#getMaxSize() max size} of the presentation
* spec returned by {@link InlineSuggestionInfo#getPresentationSpec()}.
*
- * @param context Context in which to inflate the view.
- * @param size The size at which to inflate the suggestion.
- * @param callback Callback for receiving the inflated view.
+ * <p> The caller can attach an {@link View.OnClickListener} and/or an
+ * {@link View.OnLongClickListener} to the view in the {@code callback} to receive click and
+ * long click events on the view.
*
+ * @param context Context in which to inflate the view.
+ * @param size The size at which to inflate the suggestion.
+ * @param callback Callback for receiving the inflated view.
* @throws IllegalArgumentException If an invalid argument is passed.
- * @throws IllegalStateException if this method is already called.
+ * @throws IllegalStateException If this method is already called.
*/
public void inflate(@NonNull Context context, @NonNull Size size,
@NonNull @CallbackExecutor Executor callbackExecutor,
@@ -151,12 +154,31 @@
}
@Override
+ @BinderThread
public void onContent(SurfaceControlViewHost.SurfacePackage content) {
final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get();
if (callbackImpl != null) {
callbackImpl.onContent(content);
}
}
+
+ @Override
+ @BinderThread
+ public void onClick() {
+ final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get();
+ if (callbackImpl != null) {
+ callbackImpl.onClick();
+ }
+ }
+
+ @Override
+ @BinderThread
+ public void onLongClick() {
+ final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get();
+ if (callbackImpl != null) {
+ callbackImpl.onLongClick();
+ }
+ }
}
private static final class InlineContentCallbackImpl {
@@ -164,6 +186,7 @@
private final @NonNull Context mContext;
private final @NonNull Executor mCallbackExecutor;
private final @NonNull Consumer<View> mCallback;
+ private @Nullable View mView;
InlineContentCallbackImpl(@NonNull Context context,
@NonNull @CallbackExecutor Executor callbackExecutor,
@@ -173,12 +196,27 @@
mCallback = callback;
}
+ @BinderThread
public void onContent(SurfaceControlViewHost.SurfacePackage content) {
if (content == null) {
mCallbackExecutor.execute(() -> mCallback.accept(/* view */null));
} else {
- mCallbackExecutor.execute(
- () -> mCallback.accept(new InlineContentView(mContext, content)));
+ mView = new InlineContentView(mContext, content);
+ mCallbackExecutor.execute(() -> mCallback.accept(mView));
+ }
+ }
+
+ @BinderThread
+ public void onClick() {
+ if (mView != null && mView.hasOnClickListeners()) {
+ mView.callOnClick();
+ }
+ }
+
+ @BinderThread
+ public void onLongClick() {
+ if (mView != null && mView.hasOnLongClickListeners()) {
+ mView.performLongClick();
}
}
}
@@ -201,7 +239,7 @@
- // Code below generated by codegen v1.0.14.
+ // Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -360,8 +398,8 @@
};
@DataClass.Generated(
- time = 1581929285156L,
- codegenVersion = "1.0.14",
+ time = 1583889058241L,
+ codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index e50da40..2945a86 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -44,15 +44,15 @@
public static final int SUGGESTION_COUNT_UNLIMITED = Integer.MAX_VALUE;
/**
- * Max number of suggestions expected from the response. Defaults to {@code
- * SUGGESTION_COUNT_UNLIMITED} if not set.
+ * Max number of suggestions expected from the response. It must be a positive value.
+ * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
*/
private final int mMaxSuggestionCount;
/**
* The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
* count is larger than the number of specs in the list, then the last spec is used for the
- * remainder of the suggestions.
+ * remainder of the suggestions. The list should not be empty.
*/
private final @NonNull List<InlinePresentationSpec> mPresentationSpecs;
@@ -117,6 +117,7 @@
}
private void onConstructed() {
+ Preconditions.checkState(!mPresentationSpecs.isEmpty());
Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
}
@@ -162,7 +163,7 @@
- // Code below generated by codegen v1.0.14.
+ // Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -202,8 +203,8 @@
}
/**
- * Max number of suggestions expected from the response. Defaults to {@code
- * SUGGESTION_COUNT_UNLIMITED} if not set.
+ * Max number of suggestions expected from the response. It must be a positive value.
+ * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
*/
@DataClass.Generated.Member
public int getMaxSuggestionCount() {
@@ -213,7 +214,7 @@
/**
* The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
* count is larger than the number of specs in the list, then the last spec is used for the
- * remainder of the suggestions.
+ * remainder of the suggestions. The list should not be empty.
*/
@DataClass.Generated.Member
public @NonNull List<InlinePresentationSpec> getPresentationSpecs() {
@@ -419,7 +420,7 @@
* @param presentationSpecs
* The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
* count is larger than the number of specs in the list, then the last spec is used for the
- * remainder of the suggestions.
+ * remainder of the suggestions. The list should not be empty.
*/
public Builder(
@NonNull List<InlinePresentationSpec> presentationSpecs) {
@@ -429,8 +430,8 @@
}
/**
- * Max number of suggestions expected from the response. Defaults to {@code
- * SUGGESTION_COUNT_UNLIMITED} if not set.
+ * Max number of suggestions expected from the response. It must be a positive value.
+ * Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
*/
@DataClass.Generated.Member
public @NonNull Builder setMaxSuggestionCount(int value) {
@@ -443,7 +444,7 @@
/**
* The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
* count is larger than the number of specs in the list, then the last spec is used for the
- * remainder of the suggestions.
+ * remainder of the suggestions. The list should not be empty.
*/
@DataClass.Generated.Member
@Override
@@ -495,7 +496,7 @@
* The extras state propagated from the IME to pass extra data.
*/
@DataClass.Generated.Member
- public @NonNull Builder setExtras(@Nullable Bundle value) {
+ public @NonNull Builder setExtras(@NonNull Bundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
mExtras = value;
@@ -510,7 +511,7 @@
*/
@DataClass.Generated.Member
@Override
- @NonNull Builder setHostInputToken(@Nullable IBinder value) {
+ @NonNull Builder setHostInputToken(@NonNull IBinder value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
mHostInputToken = value;
@@ -575,8 +576,8 @@
}
@DataClass.Generated(
- time = 1582339908980L,
- codegenVersion = "1.0.14",
+ time = 1584067152935L,
+ codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
diff --git a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
deleted file mode 100644
index 3164567..0000000
--- a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.KeyValueListParser;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-import java.lang.ref.WeakReference;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-/**
- * Parses the {@link Settings.Global#TEXT_CLASSIFIER_ACTION_MODEL_PARAMS} flag.
- *
- * @hide
- */
-public final class ActionsModelParamsSupplier implements
- Supplier<ActionsModelParamsSupplier.ActionsModelParams> {
- private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
-
- @VisibleForTesting
- static final String KEY_REQUIRED_MODEL_VERSION = "required_model_version";
- @VisibleForTesting
- static final String KEY_REQUIRED_LOCALES = "required_locales";
- @VisibleForTesting
- static final String KEY_SERIALIZED_PRECONDITIONS = "serialized_preconditions";
-
- private final Context mAppContext;
- private final SettingsObserver mSettingsObserver;
-
- private final Object mLock = new Object();
- private final Runnable mOnChangedListener;
- @Nullable
- @GuardedBy("mLock")
- private ActionsModelParams mActionsModelParams;
- @GuardedBy("mLock")
- private boolean mParsed = true;
-
- public ActionsModelParamsSupplier(Context context, @Nullable Runnable onChangedListener) {
- final Context appContext = Preconditions.checkNotNull(context).getApplicationContext();
- // Some contexts don't have an app context.
- mAppContext = appContext != null ? appContext : context;
- mOnChangedListener = onChangedListener == null ? () -> {} : onChangedListener;
- mSettingsObserver = new SettingsObserver(mAppContext, () -> {
- synchronized (mLock) {
- Log.v(TAG, "Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS is updated");
- mParsed = true;
- mOnChangedListener.run();
- }
- });
- }
-
- /**
- * Returns the parsed actions params or {@link ActionsModelParams#INVALID} if the value is
- * invalid.
- */
- @Override
- public ActionsModelParams get() {
- synchronized (mLock) {
- if (mParsed) {
- mActionsModelParams = parse(mAppContext.getContentResolver());
- mParsed = false;
- }
- }
- return mActionsModelParams;
- }
-
- private ActionsModelParams parse(ContentResolver contentResolver) {
- String settingStr = Settings.Global.getString(contentResolver,
- Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS);
- if (TextUtils.isEmpty(settingStr)) {
- return ActionsModelParams.INVALID;
- }
- try {
- KeyValueListParser keyValueListParser = new KeyValueListParser(',');
- keyValueListParser.setString(settingStr);
- int version = keyValueListParser.getInt(KEY_REQUIRED_MODEL_VERSION, -1);
- if (version == -1) {
- Log.w(TAG, "ActionsModelParams.Parse, invalid model version");
- return ActionsModelParams.INVALID;
- }
- String locales = keyValueListParser.getString(KEY_REQUIRED_LOCALES, null);
- if (locales == null) {
- Log.w(TAG, "ActionsModelParams.Parse, invalid locales");
- return ActionsModelParams.INVALID;
- }
- String serializedPreconditionsStr =
- keyValueListParser.getString(KEY_SERIALIZED_PRECONDITIONS, null);
- if (serializedPreconditionsStr == null) {
- Log.w(TAG, "ActionsModelParams.Parse, invalid preconditions");
- return ActionsModelParams.INVALID;
- }
- byte[] serializedPreconditions =
- Base64.decode(serializedPreconditionsStr, Base64.NO_WRAP);
- return new ActionsModelParams(version, locales, serializedPreconditions);
- } catch (Throwable t) {
- Log.e(TAG, "Invalid TEXT_CLASSIFIER_ACTION_MODEL_PARAMS, ignore", t);
- }
- return ActionsModelParams.INVALID;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- mAppContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
- } finally {
- super.finalize();
- }
- }
-
- /**
- * Represents the parsed result.
- */
- public static final class ActionsModelParams {
-
- public static final ActionsModelParams INVALID =
- new ActionsModelParams(-1, "", new byte[0]);
-
- /**
- * The required model version to apply {@code mSerializedPreconditions}.
- */
- private final int mRequiredModelVersion;
-
- /**
- * The required model locales to apply {@code mSerializedPreconditions}.
- */
- private final String mRequiredModelLocales;
-
- /**
- * The serialized params that will be applied to the model file, if all requirements are
- * met. Do not modify.
- */
- private final byte[] mSerializedPreconditions;
-
- public ActionsModelParams(int requiredModelVersion, String requiredModelLocales,
- byte[] serializedPreconditions) {
- mRequiredModelVersion = requiredModelVersion;
- mRequiredModelLocales = Preconditions.checkNotNull(requiredModelLocales);
- mSerializedPreconditions = Preconditions.checkNotNull(serializedPreconditions);
- }
-
- /**
- * Returns the serialized preconditions. Returns {@code null} if the the model in use does
- * not meet all the requirements listed in the {@code ActionsModelParams} or the params
- * are invalid.
- */
- @Nullable
- public byte[] getSerializedPreconditions(ModelFileManager.ModelFile modelInUse) {
- if (this == INVALID) {
- return null;
- }
- if (modelInUse.getVersion() != mRequiredModelVersion) {
- Log.w(TAG, String.format(
- "Not applying mSerializedPreconditions, required version=%d, actual=%d",
- mRequiredModelVersion, modelInUse.getVersion()));
- return null;
- }
- if (!Objects.equals(modelInUse.getSupportedLocalesStr(), mRequiredModelLocales)) {
- Log.w(TAG, String.format(
- "Not applying mSerializedPreconditions, required locales=%s, actual=%s",
- mRequiredModelLocales, modelInUse.getSupportedLocalesStr()));
- return null;
- }
- return mSerializedPreconditions;
- }
- }
-
- private static final class SettingsObserver extends ContentObserver {
-
- private final WeakReference<Runnable> mOnChangedListener;
-
- SettingsObserver(Context appContext, Runnable listener) {
- super(null);
- mOnChangedListener = new WeakReference<>(listener);
- appContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS),
- false /* notifyForDescendants */,
- this);
- }
-
- public void onChange(boolean selfChange) {
- if (mOnChangedListener.get() != null) {
- mOnChangedListener.get().run();
- }
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
deleted file mode 100644
index 3ed48f6..0000000
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.app.Person;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Pair;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.StringJoiner;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * Helper class for action suggestions.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class ActionsSuggestionsHelper {
- private static final String TAG = "ActionsSuggestions";
- private static final int USER_LOCAL = 0;
- private static final int FIRST_NON_LOCAL_USER = 1;
-
- private ActionsSuggestionsHelper() {}
-
- /**
- * Converts the messages to a list of native messages object that the model can understand.
- * <p>
- * User id encoding - local user is represented as 0, Other users are numbered according to
- * how far before they spoke last time in the conversation. For example, considering this
- * conversation:
- * <ul>
- * <li> User A: xxx
- * <li> Local user: yyy
- * <li> User B: zzz
- * </ul>
- * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
- */
- public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
- List<ConversationActions.Message> messages,
- Function<CharSequence, String> languageDetector) {
- List<ConversationActions.Message> messagesWithText =
- messages.stream()
- .filter(message -> !TextUtils.isEmpty(message.getText()))
- .collect(Collectors.toCollection(ArrayList::new));
- if (messagesWithText.isEmpty()) {
- return new ActionsSuggestionsModel.ConversationMessage[0];
- }
- Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
- PersonEncoder personEncoder = new PersonEncoder();
- int size = messagesWithText.size();
- for (int i = size - 1; i >= 0; i--) {
- ConversationActions.Message message = messagesWithText.get(i);
- long referenceTime = message.getReferenceTime() == null
- ? 0
- : message.getReferenceTime().toInstant().toEpochMilli();
- String timeZone = message.getReferenceTime() == null
- ? null
- : message.getReferenceTime().getZone().getId();
- nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
- personEncoder.encode(message.getAuthor()),
- message.getText().toString(), referenceTime, timeZone,
- languageDetector.apply(message.getText())));
- }
- return nativeMessages.toArray(
- new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
- }
-
- /**
- * Returns the result id for logging.
- */
- public static String createResultId(
- Context context,
- List<ConversationActions.Message> messages,
- int modelVersion,
- List<Locale> modelLocales) {
- final StringJoiner localesJoiner = new StringJoiner(",");
- for (Locale locale : modelLocales) {
- localesJoiner.add(locale.toLanguageTag());
- }
- final String modelName = String.format(
- Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion);
- final int hash = Objects.hash(
- messages.stream().mapToInt(ActionsSuggestionsHelper::hashMessage),
- context.getPackageName(),
- System.currentTimeMillis());
- return SelectionSessionLogger.SignatureParser.createSignature(
- SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
- }
-
- /**
- * Generated labeled intent from an action suggestion and return the resolved result.
- */
- @Nullable
- public static LabeledIntent.Result createLabeledIntentResult(
- Context context,
- TemplateIntentFactory templateIntentFactory,
- ActionsSuggestionsModel.ActionSuggestion nativeSuggestion) {
- RemoteActionTemplate[] remoteActionTemplates =
- nativeSuggestion.getRemoteActionTemplates();
- if (remoteActionTemplates == null) {
- Log.w(TAG, "createRemoteAction: Missing template for type "
- + nativeSuggestion.getActionType());
- return null;
- }
- List<LabeledIntent> labeledIntents = templateIntentFactory.create(remoteActionTemplates);
- if (labeledIntents.isEmpty()) {
- return null;
- }
- // Given that we only support implicit intent here, we should expect there is just one
- // intent for each action type.
- LabeledIntent.TitleChooser titleChooser =
- ActionsSuggestionsHelper.createTitleChooser(nativeSuggestion.getActionType());
- return labeledIntents.get(0).resolve(context, titleChooser, null);
- }
-
- /**
- * Returns a {@link LabeledIntent.TitleChooser} for conversation actions use case.
- */
- @Nullable
- public static LabeledIntent.TitleChooser createTitleChooser(String actionType) {
- if (ConversationAction.TYPE_OPEN_URL.equals(actionType)) {
- return (labeledIntent, resolveInfo) -> {
- if (resolveInfo.handleAllWebDataURI) {
- return labeledIntent.titleWithEntity;
- }
- if ("android".equals(resolveInfo.activityInfo.packageName)) {
- return labeledIntent.titleWithEntity;
- }
- return labeledIntent.titleWithoutEntity;
- };
- }
- return null;
- }
-
- /**
- * Returns a list of {@link ConversationAction}s that have 0 duplicates. Two actions are
- * duplicates if they may look the same to users. This function assumes every
- * ConversationActions with a non-null RemoteAction also have a non-null intent in the extras.
- */
- public static List<ConversationAction> removeActionsWithDuplicates(
- List<ConversationAction> conversationActions) {
- // Ideally, we should compare title and icon here, but comparing icon is expensive and thus
- // we use the component name of the target handler as the heuristic.
- Map<Pair<String, String>, Integer> counter = new ArrayMap<>();
- for (ConversationAction conversationAction : conversationActions) {
- Pair<String, String> representation = getRepresentation(conversationAction);
- if (representation == null) {
- continue;
- }
- Integer existingCount = counter.getOrDefault(representation, 0);
- counter.put(representation, existingCount + 1);
- }
- List<ConversationAction> result = new ArrayList<>();
- for (ConversationAction conversationAction : conversationActions) {
- Pair<String, String> representation = getRepresentation(conversationAction);
- if (representation == null || counter.getOrDefault(representation, 0) == 1) {
- result.add(conversationAction);
- }
- }
- return result;
- }
-
- @Nullable
- private static Pair<String, String> getRepresentation(
- ConversationAction conversationAction) {
- RemoteAction remoteAction = conversationAction.getAction();
- if (remoteAction == null) {
- return null;
- }
- Intent actionIntent = ExtrasUtils.getActionIntent(conversationAction.getExtras());
- ComponentName componentName = actionIntent.getComponent();
- // Action without a component name will be considered as from the same app.
- String packageName = componentName == null ? null : componentName.getPackageName();
- return new Pair<>(
- conversationAction.getAction().getTitle().toString(), packageName);
- }
-
- private static final class PersonEncoder {
- private final Map<Person, Integer> mMapping = new ArrayMap<>();
- private int mNextUserId = FIRST_NON_LOCAL_USER;
-
- private int encode(Person person) {
- if (ConversationActions.Message.PERSON_USER_SELF.equals(person)) {
- return USER_LOCAL;
- }
- Integer result = mMapping.get(person);
- if (result == null) {
- mMapping.put(person, mNextUserId);
- result = mNextUserId;
- mNextUserId++;
- }
- return result;
- }
- }
-
- private static int hashMessage(ConversationActions.Message message) {
- return Objects.hash(message.getAuthor(), message.getText(), message.getReferenceTime());
- }
-}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 6246b50..842ba29 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -21,15 +21,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
-import android.annotation.UserIdInt;
import android.app.Person;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.text.SpannedString;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -317,13 +314,9 @@
@NonNull
@Hint
private final List<String> mHints;
- @Nullable
- private String mCallingPackageName;
- @UserIdInt
- private int mUserId = UserHandle.USER_NULL;
@NonNull
private Bundle mExtras;
- private boolean mUseDefaultTextClassifier;
+ @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
private Request(
@NonNull List<Message> conversation,
@@ -345,10 +338,8 @@
int maxSuggestions = in.readInt();
List<String> hints = new ArrayList<>();
in.readStringList(hints);
- String callingPackageName = in.readString();
- int userId = in.readInt();
Bundle extras = in.readBundle();
- boolean useDefaultTextClassifier = in.readBoolean();
+ SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
Request request = new Request(
conversation,
@@ -356,9 +347,7 @@
maxSuggestions,
hints,
extras);
- request.setCallingPackageName(callingPackageName);
- request.setUserId(userId);
- request.setUseDefaultTextClassifier(useDefaultTextClassifier);
+ request.setSystemTextClassifierMetadata(systemTcMetadata);
return request;
}
@@ -368,10 +357,8 @@
parcel.writeParcelable(mTypeConfig, flags);
parcel.writeInt(mMaxSuggestions);
parcel.writeStringList(mHints);
- parcel.writeString(mCallingPackageName);
- parcel.writeInt(mUserId);
parcel.writeBundle(mExtras);
- parcel.writeBoolean(mUseDefaultTextClassifier);
+ parcel.writeParcelable(mSystemTcMetadata, flags);
}
@Override
@@ -421,62 +408,31 @@
}
/**
- * Sets the name of the package that is sending this request.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void setCallingPackageName(@Nullable String callingPackageName) {
- mCallingPackageName = callingPackageName;
- }
-
- /**
* Returns the name of the package that sent this request.
* This returns {@code null} if no calling package name is set.
*/
@Nullable
public String getCallingPackageName() {
- return mCallingPackageName;
+ return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
}
/**
- * Sets the id of the user that sent this request.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- void setUserId(@UserIdInt int userId) {
- mUserId = userId;
- }
-
- /**
- * Returns the id of the user that sent this request.
- * @hide
- */
- @UserIdInt
- public int getUserId() {
- return mUserId;
- }
-
- /**
- * Sets whether to use the default text classifier to handle this request.
- * This will be ignored if it is not the system text classifier to handle this request.
+ * Sets the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
- mUseDefaultTextClassifier = useDefaultTextClassifier;
+ void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcData) {
+ mSystemTcMetadata = systemTcData;
}
/**
- * Returns whether to use the default text classifier to handle this request. This
- * will be ignored if it is not the system text classifier to handle this request.
+ * Returns the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- public boolean getUseDefaultTextClassifier() {
- return mUseDefaultTextClassifier;
+ @Nullable
+ public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+ return mSystemTcMetadata;
}
/**
diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java
index 11e0e2c..9e2b642 100644
--- a/core/java/android/view/textclassifier/ExtrasUtils.java
+++ b/core/java/android/view/textclassifier/ExtrasUtils.java
@@ -19,15 +19,9 @@
import android.annotation.Nullable;
import android.app.RemoteAction;
import android.content.Intent;
-import android.icu.util.ULocale;
import android.os.Bundle;
-import com.android.internal.util.ArrayUtils;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
import java.util.ArrayList;
-import java.util.List;
/**
* Utility class for inserting and retrieving data in TextClassifier request/response extras.
@@ -37,52 +31,19 @@
public final class ExtrasUtils {
// Keys for response objects.
- private static final String SERIALIZED_ENTITIES_DATA = "serialized-entities-data";
- private static final String ENTITIES_EXTRAS = "entities-extras";
private static final String ACTION_INTENT = "action-intent";
private static final String ACTIONS_INTENTS = "actions-intents";
private static final String FOREIGN_LANGUAGE = "foreign-language";
private static final String ENTITY_TYPE = "entity-type";
private static final String SCORE = "score";
- private static final String MODEL_VERSION = "model-version";
private static final String MODEL_NAME = "model-name";
- private static final String TEXT_LANGUAGES = "text-languages";
- private static final String ENTITIES = "entities";
- // Keys for request objects.
- private static final String IS_SERIALIZED_ENTITY_DATA_ENABLED =
- "is-serialized-entity-data-enabled";
-
- private ExtrasUtils() {}
-
- /**
- * Bundles and returns foreign language detection information for TextClassifier responses.
- */
- static Bundle createForeignLanguageExtra(
- String language, float score, int modelVersion) {
- final Bundle bundle = new Bundle();
- bundle.putString(ENTITY_TYPE, language);
- bundle.putFloat(SCORE, score);
- bundle.putInt(MODEL_VERSION, modelVersion);
- bundle.putString(MODEL_NAME, "langId_v" + modelVersion);
- return bundle;
- }
-
- /**
- * Stores {@code extra} as foreign language information in TextClassifier response object's
- * extras {@code container}.
- *
- * @see #getForeignLanguageExtra(TextClassification)
- */
- static void putForeignLanguageExtra(Bundle container, Bundle extra) {
- container.putParcelable(FOREIGN_LANGUAGE, extra);
+ private ExtrasUtils() {
}
/**
* Returns foreign language detection information contained in the TextClassification object.
* responses.
- *
- * @see #putForeignLanguageExtra(Bundle, Bundle)
*/
@Nullable
public static Bundle getForeignLanguageExtra(@Nullable TextClassification classification) {
@@ -93,72 +54,6 @@
}
/**
- * @see #getTopLanguage(Intent)
- */
- static void putTopLanguageScores(Bundle container, EntityConfidence languageScores) {
- final int maxSize = Math.min(3, languageScores.getEntities().size());
- final String[] languages = languageScores.getEntities().subList(0, maxSize)
- .toArray(new String[0]);
- final float[] scores = new float[languages.length];
- for (int i = 0; i < languages.length; i++) {
- scores[i] = languageScores.getConfidenceScore(languages[i]);
- }
- container.putStringArray(ENTITY_TYPE, languages);
- container.putFloatArray(SCORE, scores);
- }
-
- /**
- * @see #putTopLanguageScores(Bundle, EntityConfidence)
- */
- @Nullable
- public static ULocale getTopLanguage(@Nullable Intent intent) {
- if (intent == null) {
- return null;
- }
- final Bundle tcBundle = intent.getBundleExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER);
- if (tcBundle == null) {
- return null;
- }
- final Bundle textLanguagesExtra = tcBundle.getBundle(TEXT_LANGUAGES);
- if (textLanguagesExtra == null) {
- return null;
- }
- final String[] languages = textLanguagesExtra.getStringArray(ENTITY_TYPE);
- final float[] scores = textLanguagesExtra.getFloatArray(SCORE);
- if (languages == null || scores == null
- || languages.length == 0 || languages.length != scores.length) {
- return null;
- }
- int highestScoringIndex = 0;
- for (int i = 1; i < languages.length; i++) {
- if (scores[highestScoringIndex] < scores[i]) {
- highestScoringIndex = i;
- }
- }
- return ULocale.forLanguageTag(languages[highestScoringIndex]);
- }
-
- public static void putTextLanguagesExtra(Bundle container, Bundle extra) {
- container.putBundle(TEXT_LANGUAGES, extra);
- }
-
- /**
- * Stores {@code actionIntents} information in TextClassifier response object's extras
- * {@code container}.
- */
- static void putActionsIntents(Bundle container, ArrayList<Intent> actionsIntents) {
- container.putParcelableArrayList(ACTIONS_INTENTS, actionsIntents);
- }
-
- /**
- * Stores {@code actionIntents} information in TextClassifier response object's extras
- * {@code container}.
- */
- public static void putActionIntent(Bundle container, @Nullable Intent actionIntent) {
- container.putParcelable(ACTION_INTENT, actionIntent);
- }
-
- /**
* Returns {@code actionIntent} information contained in a TextClassifier response object.
*/
@Nullable
@@ -167,48 +62,6 @@
}
/**
- * Stores serialized entity data information in TextClassifier response object's extras
- * {@code container}.
- */
- public static void putSerializedEntityData(
- Bundle container, @Nullable byte[] serializedEntityData) {
- container.putByteArray(SERIALIZED_ENTITIES_DATA, serializedEntityData);
- }
-
- /**
- * Returns serialized entity data information contained in a TextClassifier response
- * object.
- */
- @Nullable
- public static byte[] getSerializedEntityData(Bundle container) {
- return container.getByteArray(SERIALIZED_ENTITIES_DATA);
- }
-
- /**
- * Stores {@code entities} information in TextClassifier response object's extras
- * {@code container}.
- *
- * @see {@link #getCopyText(Bundle)}
- */
- public static void putEntitiesExtras(Bundle container, @Nullable Bundle entitiesExtras) {
- container.putParcelable(ENTITIES_EXTRAS, entitiesExtras);
- }
-
- /**
- * Returns {@code entities} information contained in a TextClassifier response object.
- *
- * @see {@link #putEntitiesExtras(Bundle, Bundle)}
- */
- @Nullable
- public static String getCopyText(Bundle container) {
- Bundle entitiesExtras = container.getParcelable(ENTITIES_EXTRAS);
- if (entitiesExtras == null) {
- return null;
- }
- return entitiesExtras.getString("text");
- }
-
- /**
* Returns {@code actionIntents} information contained in the TextClassification object.
*/
@Nullable
@@ -224,7 +77,7 @@
* action string, {@code intentAction}.
*/
@Nullable
- public static RemoteAction findAction(
+ private static RemoteAction findAction(
@Nullable TextClassification classification, @Nullable String intentAction) {
if (classification == null || intentAction == null) {
return null;
@@ -283,53 +136,4 @@
}
return extra.getString(MODEL_NAME);
}
-
- /**
- * Stores the entities from {@link AnnotatorModel.ClassificationResult} in {@code container}.
- */
- public static void putEntities(
- Bundle container,
- @Nullable AnnotatorModel.ClassificationResult[] classifications) {
- if (ArrayUtils.isEmpty(classifications)) {
- return;
- }
- ArrayList<Bundle> entitiesBundle = new ArrayList<>();
- for (AnnotatorModel.ClassificationResult classification : classifications) {
- if (classification == null) {
- continue;
- }
- Bundle entityBundle = new Bundle();
- entityBundle.putString(ENTITY_TYPE, classification.getCollection());
- entityBundle.putByteArray(
- SERIALIZED_ENTITIES_DATA,
- classification.getSerializedEntityData());
- entitiesBundle.add(entityBundle);
- }
- if (!entitiesBundle.isEmpty()) {
- container.putParcelableArrayList(ENTITIES, entitiesBundle);
- }
- }
-
- /**
- * Returns a list of entities contained in the {@code extra}.
- */
- @Nullable
- public static List<Bundle> getEntities(Bundle container) {
- return container.getParcelableArrayList(ENTITIES);
- }
-
- /**
- * Whether the annotator should populate serialized entity data into the result object.
- */
- public static boolean isSerializedEntityDataEnabled(TextLinks.Request request) {
- return request.getExtras().getBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED);
- }
-
- /**
- * To indicate whether the annotator should populate serialized entity data in the result
- * object.
- */
- public static void putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled) {
- bundle.putBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED, isEnabled);
- }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/GenerateLinksLogger.java b/core/java/android/view/textclassifier/GenerateLinksLogger.java
deleted file mode 100644
index 17ec73a..0000000
--- a/core/java/android/view/textclassifier/GenerateLinksLogger.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.metrics.LogMaker;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Random;
-import java.util.UUID;
-
-/**
- * A helper for logging calls to generateLinks.
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class GenerateLinksLogger {
-
- private static final String LOG_TAG = "GenerateLinksLogger";
- private static final String ZERO = "0";
-
- private final MetricsLogger mMetricsLogger;
- private final Random mRng;
- private final int mSampleRate;
-
- /**
- * @param sampleRate the rate at which log events are written. (e.g. 100 means there is a 0.01
- * chance that a call to logGenerateLinks results in an event being written).
- * To write all events, pass 1.
- */
- public GenerateLinksLogger(int sampleRate) {
- mSampleRate = sampleRate;
- mRng = new Random(System.nanoTime());
- mMetricsLogger = new MetricsLogger();
- }
-
- @VisibleForTesting
- public GenerateLinksLogger(int sampleRate, MetricsLogger metricsLogger) {
- mSampleRate = sampleRate;
- mRng = new Random(System.nanoTime());
- mMetricsLogger = metricsLogger;
- }
-
- /** Logs statistics about a call to generateLinks. */
- public void logGenerateLinks(CharSequence text, TextLinks links, String callingPackageName,
- long latencyMs) {
- Objects.requireNonNull(text);
- Objects.requireNonNull(links);
- Objects.requireNonNull(callingPackageName);
- if (!shouldLog()) {
- return;
- }
-
- // Always populate the total stats, and per-entity stats for each entity type detected.
- final LinkifyStats totalStats = new LinkifyStats();
- final Map<String, LinkifyStats> perEntityTypeStats = new ArrayMap<>();
- for (TextLinks.TextLink link : links.getLinks()) {
- if (link.getEntityCount() == 0) continue;
- final String entityType = link.getEntity(0);
- if (entityType == null
- || TextClassifier.TYPE_OTHER.equals(entityType)
- || TextClassifier.TYPE_UNKNOWN.equals(entityType)) {
- continue;
- }
- totalStats.countLink(link);
- perEntityTypeStats.computeIfAbsent(entityType, k -> new LinkifyStats()).countLink(link);
- }
-
- final String callId = UUID.randomUUID().toString();
- writeStats(callId, callingPackageName, null, totalStats, text, latencyMs);
- for (Map.Entry<String, LinkifyStats> entry : perEntityTypeStats.entrySet()) {
- writeStats(callId, callingPackageName, entry.getKey(), entry.getValue(), text,
- latencyMs);
- }
- }
-
- /**
- * Returns whether this particular event should be logged.
- *
- * Sampling is used to reduce the amount of logging data generated.
- **/
- private boolean shouldLog() {
- if (mSampleRate <= 1) {
- return true;
- } else {
- return mRng.nextInt(mSampleRate) == 0;
- }
- }
-
- /** Writes a log event for the given stats. */
- private void writeStats(String callId, String callingPackageName, @Nullable String entityType,
- LinkifyStats stats, CharSequence text, long latencyMs) {
- final LogMaker log = new LogMaker(MetricsEvent.TEXT_CLASSIFIER_GENERATE_LINKS)
- .setPackageName(callingPackageName)
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID, callId)
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS, stats.mNumLinks)
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH, stats.mNumLinksTextLength)
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH, text.length())
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY, latencyMs);
- if (entityType != null) {
- log.addTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE, entityType);
- }
- mMetricsLogger.write(log);
- debugLog(log);
- }
-
- private static void debugLog(LogMaker log) {
- if (!Log.ENABLE_FULL_LOGGING) {
- return;
- }
- final String callId = Objects.toString(
- log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
- final String entityType = Objects.toString(
- log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "ANY_ENTITY");
- final int numLinks = Integer.parseInt(
- Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS), ZERO));
- final int linkLength = Integer.parseInt(
- Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH), ZERO));
- final int textLength = Integer.parseInt(
- Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH), ZERO));
- final int latencyMs = Integer.parseInt(
- Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
-
- Log.v(LOG_TAG,
- String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
- numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
- }
-
- /** Helper class for storing per-entity type statistics. */
- private static final class LinkifyStats {
- int mNumLinks;
- int mNumLinksTextLength;
-
- void countLink(TextLinks.TextLink link) {
- mNumLinks += 1;
- mNumLinksTextLength += link.getEnd() - link.getStart();
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/Log.java b/core/java/android/view/textclassifier/Log.java
index 03ed496..98ee09c 100644
--- a/core/java/android/view/textclassifier/Log.java
+++ b/core/java/android/view/textclassifier/Log.java
@@ -32,7 +32,7 @@
* false: Limits logging to debug level.
*/
static final boolean ENABLE_FULL_LOGGING =
- android.util.Log.isLoggable(TextClassifier.DEFAULT_LOG_TAG, android.util.Log.VERBOSE);
+ android.util.Log.isLoggable(TextClassifier.LOG_TAG, android.util.Log.VERBOSE);
private Log() {
}
diff --git a/core/java/android/view/textclassifier/ModelFileManager.java b/core/java/android/view/textclassifier/ModelFileManager.java
deleted file mode 100644
index 0a4ff5d..0000000
--- a/core/java/android/view/textclassifier/ModelFileManager.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static android.view.textclassifier.TextClassifier.DEFAULT_LOG_TAG;
-
-import android.annotation.Nullable;
-import android.os.LocaleList;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.StringJoiner;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Manages model files that are listed by the model files supplier.
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class ModelFileManager {
- private final Object mLock = new Object();
- private final Supplier<List<ModelFile>> mModelFileSupplier;
-
- private List<ModelFile> mModelFiles;
-
- public ModelFileManager(Supplier<List<ModelFile>> modelFileSupplier) {
- mModelFileSupplier = Objects.requireNonNull(modelFileSupplier);
- }
-
- /**
- * Returns an unmodifiable list of model files listed by the given model files supplier.
- * <p>
- * The result is cached.
- */
- public List<ModelFile> listModelFiles() {
- synchronized (mLock) {
- if (mModelFiles == null) {
- mModelFiles = Collections.unmodifiableList(mModelFileSupplier.get());
- }
- return mModelFiles;
- }
- }
-
- /**
- * Returns the best model file for the given localelist, {@code null} if nothing is found.
- *
- * @param localeList the required locales, use {@code null} if there is no preference.
- */
- public ModelFile findBestModelFile(@Nullable LocaleList localeList) {
- final String languages = localeList == null || localeList.isEmpty()
- ? LocaleList.getDefault().toLanguageTags()
- : localeList.toLanguageTags();
- final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
-
- ModelFile bestModel = null;
- for (ModelFile model : listModelFiles()) {
- if (model.isAnyLanguageSupported(languageRangeList)) {
- if (model.isPreferredTo(bestModel)) {
- bestModel = model;
- }
- }
- }
- return bestModel;
- }
-
- /**
- * Default implementation of the model file supplier.
- */
- public static final class ModelFileSupplierImpl implements Supplier<List<ModelFile>> {
- private final File mUpdatedModelFile;
- private final File mFactoryModelDir;
- private final Pattern mModelFilenamePattern;
- private final Function<Integer, Integer> mVersionSupplier;
- private final Function<Integer, String> mSupportedLocalesSupplier;
-
- public ModelFileSupplierImpl(
- File factoryModelDir,
- String factoryModelFileNameRegex,
- File updatedModelFile,
- Function<Integer, Integer> versionSupplier,
- Function<Integer, String> supportedLocalesSupplier) {
- mUpdatedModelFile = Objects.requireNonNull(updatedModelFile);
- mFactoryModelDir = Objects.requireNonNull(factoryModelDir);
- mModelFilenamePattern = Pattern.compile(
- Objects.requireNonNull(factoryModelFileNameRegex));
- mVersionSupplier = Objects.requireNonNull(versionSupplier);
- mSupportedLocalesSupplier = Objects.requireNonNull(supportedLocalesSupplier);
- }
-
- @Override
- public List<ModelFile> get() {
- final List<ModelFile> modelFiles = new ArrayList<>();
- // The update model has the highest precedence.
- if (mUpdatedModelFile.exists()) {
- final ModelFile updatedModel = createModelFile(mUpdatedModelFile);
- if (updatedModel != null) {
- modelFiles.add(updatedModel);
- }
- }
- // Factory models should never have overlapping locales, so the order doesn't matter.
- if (mFactoryModelDir.exists() && mFactoryModelDir.isDirectory()) {
- final File[] files = mFactoryModelDir.listFiles();
- for (File file : files) {
- final Matcher matcher = mModelFilenamePattern.matcher(file.getName());
- if (matcher.matches() && file.isFile()) {
- final ModelFile model = createModelFile(file);
- if (model != null) {
- modelFiles.add(model);
- }
- }
- }
- }
- return modelFiles;
- }
-
- /** Returns null if the path did not point to a compatible model. */
- @Nullable
- private ModelFile createModelFile(File file) {
- if (!file.exists()) {
- return null;
- }
- ParcelFileDescriptor modelFd = null;
- try {
- modelFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- if (modelFd == null) {
- return null;
- }
- final int modelFdInt = modelFd.getFd();
- final int version = mVersionSupplier.apply(modelFdInt);
- final String supportedLocalesStr = mSupportedLocalesSupplier.apply(modelFdInt);
- if (supportedLocalesStr.isEmpty()) {
- Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
- return null;
- }
- final List<Locale> supportedLocales = new ArrayList<>();
- for (String langTag : supportedLocalesStr.split(",")) {
- supportedLocales.add(Locale.forLanguageTag(langTag));
- }
- return new ModelFile(
- file,
- version,
- supportedLocales,
- supportedLocalesStr,
- ModelFile.LANGUAGE_INDEPENDENT.equals(supportedLocalesStr));
- } catch (FileNotFoundException e) {
- Log.e(DEFAULT_LOG_TAG, "Failed to find " + file.getAbsolutePath(), e);
- return null;
- } finally {
- maybeCloseAndLogError(modelFd);
- }
- }
-
- /**
- * Closes the ParcelFileDescriptor, if non-null, and logs any errors that occur.
- */
- private static void maybeCloseAndLogError(@Nullable ParcelFileDescriptor fd) {
- if (fd == null) {
- return;
- }
- try {
- fd.close();
- } catch (IOException e) {
- Log.e(DEFAULT_LOG_TAG, "Error closing file.", e);
- }
- }
-
- }
-
- /**
- * Describes TextClassifier model files on disk.
- */
- public static final class ModelFile {
- public static final String LANGUAGE_INDEPENDENT = "*";
-
- private final File mFile;
- private final int mVersion;
- private final List<Locale> mSupportedLocales;
- private final String mSupportedLocalesStr;
- private final boolean mLanguageIndependent;
-
- public ModelFile(File file, int version, List<Locale> supportedLocales,
- String supportedLocalesStr,
- boolean languageIndependent) {
- mFile = Objects.requireNonNull(file);
- mVersion = version;
- mSupportedLocales = Objects.requireNonNull(supportedLocales);
- mSupportedLocalesStr = Objects.requireNonNull(supportedLocalesStr);
- mLanguageIndependent = languageIndependent;
- }
-
- /** Returns the absolute path to the model file. */
- public String getPath() {
- return mFile.getAbsolutePath();
- }
-
- /** Returns a name to use for id generation, effectively the name of the model file. */
- public String getName() {
- return mFile.getName();
- }
-
- /** Returns the version tag in the model's metadata. */
- public int getVersion() {
- return mVersion;
- }
-
- /** Returns whether the language supports any language in the given ranges. */
- public boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
- Objects.requireNonNull(languageRanges);
- return mLanguageIndependent || Locale.lookup(languageRanges, mSupportedLocales) != null;
- }
-
- /** Returns an immutable lists of supported locales. */
- public List<Locale> getSupportedLocales() {
- return Collections.unmodifiableList(mSupportedLocales);
- }
-
- /** Returns the original supported locals string read from the model file. */
- public String getSupportedLocalesStr() {
- return mSupportedLocalesStr;
- }
-
- /**
- * Returns if this model file is preferred to the given one.
- */
- public boolean isPreferredTo(@Nullable ModelFile model) {
- // A model is preferred to no model.
- if (model == null) {
- return true;
- }
-
- // A language-specific model is preferred to a language independent
- // model.
- if (!mLanguageIndependent && model.mLanguageIndependent) {
- return true;
- }
- if (mLanguageIndependent && !model.mLanguageIndependent) {
- return false;
- }
-
- // A higher-version model is preferred.
- if (mVersion > model.getVersion()) {
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getPath());
- }
-
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (other instanceof ModelFile) {
- final ModelFile otherModel = (ModelFile) other;
- return TextUtils.equals(getPath(), otherModel.getPath());
- }
- return false;
- }
-
- @Override
- public String toString() {
- final StringJoiner localesJoiner = new StringJoiner(",");
- for (Locale locale : mSupportedLocales) {
- localesJoiner.add(locale.toLanguageTag());
- }
- return String.format(Locale.US,
- "ModelFile { path=%s name=%s version=%d locales=%s }",
- getPath(), getName(), mVersion, localesJoiner.toString());
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index e0f29a9..6f9556b 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -19,10 +19,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.WidgetType;
@@ -129,7 +127,6 @@
private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN;
private @InvocationMethod int mInvocationMethod;
@Nullable private String mWidgetVersion;
- private @UserIdInt int mUserId = UserHandle.USER_NULL;
@Nullable private String mResultId;
private long mEventTime;
private long mDurationSinceSessionStart;
@@ -140,7 +137,7 @@
private int mEnd;
private int mSmartStart;
private int mSmartEnd;
- private boolean mUseDefaultTextClassifier;
+ @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
SelectionEvent(
int start, int end,
@@ -175,8 +172,7 @@
mEnd = in.readInt();
mSmartStart = in.readInt();
mSmartEnd = in.readInt();
- mUserId = in.readInt();
- mUseDefaultTextClassifier = in.readBoolean();
+ mSystemTcMetadata = in.readParcelable(null);
}
@Override
@@ -205,8 +201,7 @@
dest.writeInt(mEnd);
dest.writeInt(mSmartStart);
dest.writeInt(mSmartEnd);
- dest.writeInt(mUserId);
- dest.writeBoolean(mUseDefaultTextClassifier);
+ dest.writeParcelable(mSystemTcMetadata, flags);
}
@Override
@@ -413,41 +408,22 @@
}
/**
- * Sets the id of this event's user.
- * <p>
- * Package-private for SystemTextClassifier's use.
- */
- void setUserId(@UserIdInt int userId) {
- mUserId = userId;
- }
-
- /**
- * Returns the id of this event's user.
- * @hide
- */
- @UserIdInt
- public int getUserId() {
- return mUserId;
- }
-
- /**
- * Sets whether to use the default text classifier to handle this request.
- * This will be ignored if it is not the system text classifier to handle this request.
+ * Sets the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
- mUseDefaultTextClassifier = useDefaultTextClassifier;
+ void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) {
+ mSystemTcMetadata = systemTcMetadata;
}
/**
- * Returns whether to use the default text classifier to handle this request. This
- * will be ignored if it is not the system text classifier to handle this request.
+ * Returns the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- public boolean getUseDefaultTextClassifier() {
- return mUseDefaultTextClassifier;
+ @Nullable
+ public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+ return mSystemTcMetadata;
}
/**
@@ -476,7 +452,7 @@
mPackageName = context.getPackageName();
mWidgetType = context.getWidgetType();
mWidgetVersion = context.getWidgetVersion();
- mUserId = context.getUserId();
+ mSystemTcMetadata = context.getSystemTextClassifierMetadata();
}
/**
@@ -663,10 +639,9 @@
@Override
public int hashCode() {
return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
- mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId,
+ mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId,
mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
- mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd,
- mUseDefaultTextClassifier);
+ mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mSystemTcMetadata);
}
@Override
@@ -685,7 +660,6 @@
&& Objects.equals(mEntityType, other.mEntityType)
&& Objects.equals(mWidgetVersion, other.mWidgetVersion)
&& Objects.equals(mPackageName, other.mPackageName)
- && mUserId == other.mUserId
&& Objects.equals(mWidgetType, other.mWidgetType)
&& mInvocationMethod == other.mInvocationMethod
&& Objects.equals(mResultId, other.mResultId)
@@ -698,7 +672,7 @@
&& mEnd == other.mEnd
&& mSmartStart == other.mSmartStart
&& mSmartEnd == other.mSmartEnd
- && mUseDefaultTextClassifier == other.mUseDefaultTextClassifier;
+ && mSystemTcMetadata == other.mSystemTcMetadata;
}
@Override
@@ -706,15 +680,14 @@
return String.format(Locale.US,
"SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
+ "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
- + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
+ + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
+ "durationSincePreviousEvent=%d, eventIndex=%d,"
+ "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d, "
- + "mUseDefaultTextClassifier=%b}",
+ + "systemTcMetadata=%s}",
mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
- mUserId, mResultId, mEventTime, mDurationSinceSessionStart,
- mDurationSincePreviousEvent, mEventIndex,
- mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mUseDefaultTextClassifier);
+ mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
+ mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mSystemTcMetadata);
}
public static final @android.annotation.NonNull Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() {
diff --git a/core/java/android/view/textclassifier/SelectionSessionLogger.java b/core/java/android/view/textclassifier/SelectionSessionLogger.java
index ae9f65b..e7d896e 100644
--- a/core/java/android/view/textclassifier/SelectionSessionLogger.java
+++ b/core/java/android/view/textclassifier/SelectionSessionLogger.java
@@ -16,251 +16,24 @@
package android.view.textclassifier;
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
-import android.metrics.LogMaker;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.text.BreakIterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.StringJoiner;
/**
* A helper for logging selection session events.
+ *
* @hide
*/
public final class SelectionSessionLogger {
-
- private static final String LOG_TAG = "SelectionSessionLogger";
- static final String CLASSIFIER_ID = "androidtc";
-
- private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
- private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
- private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
- private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
- private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
- private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
- private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
- private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
- private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
- private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
- private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
- private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
-
- private static final String ZERO = "0";
- private static final String UNKNOWN = "unknown";
-
- private final MetricsLogger mMetricsLogger;
-
- public SelectionSessionLogger() {
- mMetricsLogger = new MetricsLogger();
- }
-
- @VisibleForTesting
- public SelectionSessionLogger(@NonNull MetricsLogger metricsLogger) {
- mMetricsLogger = Objects.requireNonNull(metricsLogger);
- }
-
- /** Emits a selection event to the logs. */
- public void writeEvent(@NonNull SelectionEvent event) {
- Objects.requireNonNull(event);
- final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
- .setType(getLogType(event))
- .setSubtype(getLogSubType(event))
- .setPackageName(event.getPackageName())
- .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart())
- .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent())
- .addTaggedData(INDEX, event.getEventIndex())
- .addTaggedData(WIDGET_TYPE, event.getWidgetType())
- .addTaggedData(WIDGET_VERSION, event.getWidgetVersion())
- .addTaggedData(ENTITY_TYPE, event.getEntityType())
- .addTaggedData(EVENT_START, event.getStart())
- .addTaggedData(EVENT_END, event.getEnd());
- if (isPlatformLocalTextClassifierSmartSelection(event.getResultId())) {
- // Ensure result id and smart indices are only set for events with smart selection from
- // the platform's textclassifier.
- log.addTaggedData(MODEL_NAME, SignatureParser.getModelName(event.getResultId()))
- .addTaggedData(SMART_START, event.getSmartStart())
- .addTaggedData(SMART_END, event.getSmartEnd());
- }
- if (event.getSessionId() != null) {
- log.addTaggedData(SESSION_ID, event.getSessionId().getValue());
- }
- mMetricsLogger.write(log);
- debugLog(log);
- }
-
- private static int getLogType(SelectionEvent event) {
- switch (event.getEventType()) {
- case SelectionEvent.ACTION_OVERTYPE:
- return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
- case SelectionEvent.ACTION_COPY:
- return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
- case SelectionEvent.ACTION_PASTE:
- return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
- case SelectionEvent.ACTION_CUT:
- return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
- case SelectionEvent.ACTION_SHARE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
- case SelectionEvent.ACTION_SMART_SHARE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
- case SelectionEvent.ACTION_DRAG:
- return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
- case SelectionEvent.ACTION_ABANDON:
- return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
- case SelectionEvent.ACTION_OTHER:
- return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
- case SelectionEvent.ACTION_SELECT_ALL:
- return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
- case SelectionEvent.ACTION_RESET:
- return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
- case SelectionEvent.EVENT_SELECTION_STARTED:
- return MetricsEvent.ACTION_TEXT_SELECTION_START;
- case SelectionEvent.EVENT_SELECTION_MODIFIED:
- return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
- case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
- case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
- case SelectionEvent.EVENT_AUTO_SELECTION:
- return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
- default:
- return MetricsEvent.VIEW_UNKNOWN;
- }
- }
-
- private static int getLogSubType(SelectionEvent event) {
- switch (event.getInvocationMethod()) {
- case SelectionEvent.INVOCATION_MANUAL:
- return MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL;
- case SelectionEvent.INVOCATION_LINK:
- return MetricsEvent.TEXT_SELECTION_INVOCATION_LINK;
- default:
- return MetricsEvent.TEXT_SELECTION_INVOCATION_UNKNOWN;
- }
- }
-
- private static String getLogTypeString(int logType) {
- switch (logType) {
- case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
- return "OVERTYPE";
- case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
- return "COPY";
- case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
- return "PASTE";
- case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
- return "CUT";
- case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
- return "SHARE";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
- return "SMART_SHARE";
- case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
- return "DRAG";
- case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
- return "ABANDON";
- case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
- return "OTHER";
- case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
- return "SELECT_ALL";
- case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
- return "RESET";
- case MetricsEvent.ACTION_TEXT_SELECTION_START:
- return "SELECTION_STARTED";
- case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
- return "SELECTION_MODIFIED";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
- return "SMART_SELECTION_SINGLE";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
- return "SMART_SELECTION_MULTI";
- case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
- return "AUTO_SELECTION";
- default:
- return UNKNOWN;
- }
- }
-
- private static String getLogSubTypeString(int logSubType) {
- switch (logSubType) {
- case MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL:
- return "MANUAL";
- case MetricsEvent.TEXT_SELECTION_INVOCATION_LINK:
- return "LINK";
- default:
- return UNKNOWN;
- }
- }
+ // Keep this in sync with the ResultIdUtils in libtextclassifier.
+ private static final String CLASSIFIER_ID = "androidtc";
static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
return SelectionSessionLogger.CLASSIFIER_ID.equals(
SelectionSessionLogger.SignatureParser.getClassifierId(signature));
}
- private static void debugLog(LogMaker log) {
- if (!Log.ENABLE_FULL_LOGGING) {
- return;
- }
- final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
- final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
- final String widget = widgetVersion.isEmpty()
- ? widgetType : widgetType + "-" + widgetVersion;
- final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
- if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
- String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
- sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
- Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
- }
-
- final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
- final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
- final String type = getLogTypeString(log.getType());
- final String subType = getLogSubTypeString(log.getSubtype());
- final int smartStart = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_START), ZERO));
- final int smartEnd = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_END), ZERO));
- final int eventStart = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_START), ZERO));
- final int eventEnd = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_END), ZERO));
-
- Log.v(LOG_TAG,
- String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
- index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd,
- widget, model));
- }
-
- /**
- * Returns a token iterator for tokenizing text for logging purposes.
- */
- public static BreakIterator getTokenIterator(@NonNull Locale locale) {
- return BreakIterator.getWordInstance(Objects.requireNonNull(locale));
- }
-
- /**
- * Creates a string id that may be used to identify a TextClassifier result.
- */
- public static String createId(
- String text, int start, int end, Context context, int modelVersion,
- List<Locale> locales) {
- Objects.requireNonNull(text);
- Objects.requireNonNull(context);
- Objects.requireNonNull(locales);
- final StringJoiner localesJoiner = new StringJoiner(",");
- for (Locale locale : locales) {
- localesJoiner.add(locale.toLanguageTag());
- }
- final String modelName = String.format(Locale.US, "%s_v%d", localesJoiner.toString(),
- modelVersion);
- final int hash = Objects.hash(text, start, end, context.getPackageName());
- return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash);
- }
-
/**
* Helper for creating and parsing string ids for
* {@link android.view.textclassifier.TextClassifierImpl}.
@@ -268,10 +41,6 @@
@VisibleForTesting
public static final class SignatureParser {
- static String createSignature(String classifierId, String modelName, int hash) {
- return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash);
- }
-
static String getClassifierId(@Nullable String signature) {
if (signature == null) {
return "";
@@ -282,29 +51,5 @@
}
return "";
}
-
- static String getModelName(@Nullable String signature) {
- if (signature == null) {
- return "";
- }
- final int start = signature.indexOf("|") + 1;
- final int end = signature.indexOf("|", start);
- if (start >= 1 && end >= start) {
- return signature.substring(start, end);
- }
- return "";
- }
-
- static int getHash(@Nullable String signature) {
- if (signature == null) {
- return 0;
- }
- final int index1 = signature.indexOf("|");
- final int index2 = signature.indexOf("|", index1);
- if (index2 > 0) {
- return Integer.parseInt(signature.substring(index2));
- }
- return 0;
- }
}
}
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index fe5e8d6..8eac1c1 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.content.Context;
import android.os.Bundle;
@@ -39,25 +38,31 @@
import java.util.concurrent.TimeUnit;
/**
- * Proxy to the system's default TextClassifier.
+ * proxy to the request to TextClassifierService via the TextClassificationManagerService.
+ *
* @hide
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public final class SystemTextClassifier implements TextClassifier {
- private static final String LOG_TAG = "SystemTextClassifier";
+ private static final String LOG_TAG = TextClassifier.LOG_TAG;
private final ITextClassifierService mManagerService;
private final TextClassificationConstants mSettings;
private final TextClassifier mFallback;
- private final String mPackageName;
- // NOTE: Always set this before sending a request to the manager service otherwise the manager
- // service will throw a remote exception.
- @UserIdInt
- private final int mUserId;
- private final boolean mUseDefault;
private TextClassificationSessionId mSessionId;
+ // NOTE: Always set this before sending a request to the manager service otherwise the
+ // manager service will throw a remote exception.
+ @NonNull
+ private final SystemTextClassifierMetadata mSystemTcMetadata;
+ /**
+ * Constructor of {@link SystemTextClassifier}
+ *
+ * @param context the context of the request.
+ * @param settings TextClassifier specific settings.
+ * @param useDefault whether to use the default text classifier to handle this request
+ */
public SystemTextClassifier(
Context context,
TextClassificationConstants settings,
@@ -66,9 +71,11 @@
ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
mSettings = Objects.requireNonNull(settings);
mFallback = TextClassifier.NO_OP;
- mPackageName = Objects.requireNonNull(context.getOpPackageName());
- mUserId = context.getUserId();
- mUseDefault = useDefault;
+ // NOTE: Always set this before sending a request to the manager service otherwise the
+ // manager service will throw a remote exception.
+ mSystemTcMetadata = new SystemTextClassifierMetadata(
+ Objects.requireNonNull(context.getOpPackageName()), context.getUserId(),
+ useDefault);
}
/**
@@ -80,9 +87,7 @@
Objects.requireNonNull(request);
Utils.checkMainThread();
try {
- request.setCallingPackageName(mPackageName);
- request.setUserId(mUserId);
- request.setUseDefaultTextClassifier(mUseDefault);
+ request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextSelection> callback =
new BlockingCallback<>("textselection");
mManagerService.onSuggestSelection(mSessionId, request, callback);
@@ -105,9 +110,7 @@
Objects.requireNonNull(request);
Utils.checkMainThread();
try {
- request.setCallingPackageName(mPackageName);
- request.setUserId(mUserId);
- request.setUseDefaultTextClassifier(mUseDefault);
+ request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextClassification> callback =
new BlockingCallback<>("textclassification");
mManagerService.onClassifyText(mSessionId, request, callback);
@@ -137,9 +140,7 @@
}
try {
- request.setCallingPackageName(mPackageName);
- request.setUserId(mUserId);
- request.setUseDefaultTextClassifier(mUseDefault);
+ request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextLinks> callback =
new BlockingCallback<>("textlinks");
mManagerService.onGenerateLinks(mSessionId, request, callback);
@@ -159,8 +160,7 @@
Utils.checkMainThread();
try {
- event.setUserId(mUserId);
- event.setUseDefaultTextClassifier(mUseDefault);
+ event.setSystemTextClassifierMetadata(mSystemTcMetadata);
mManagerService.onSelectionEvent(mSessionId, event);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error reporting selection event.", e);
@@ -173,12 +173,11 @@
Utils.checkMainThread();
try {
- final TextClassificationContext tcContext = event.getEventContext() == null
- ? new TextClassificationContext.Builder(mPackageName, WIDGET_TYPE_UNKNOWN)
- .build()
- : event.getEventContext();
- tcContext.setUserId(mUserId);
- tcContext.setUseDefaultTextClassifier(mUseDefault);
+ final TextClassificationContext tcContext =
+ event.getEventContext() == null ? new TextClassificationContext.Builder(
+ mSystemTcMetadata.getCallingPackageName(), WIDGET_TYPE_UNKNOWN).build()
+ : event.getEventContext();
+ tcContext.setSystemTextClassifierMetadata(mSystemTcMetadata);
event.setEventContext(tcContext);
mManagerService.onTextClassifierEvent(mSessionId, event);
} catch (RemoteException e) {
@@ -192,9 +191,7 @@
Utils.checkMainThread();
try {
- request.setCallingPackageName(mPackageName);
- request.setUserId(mUserId);
- request.setUseDefaultTextClassifier(mUseDefault);
+ request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextLanguage> callback =
new BlockingCallback<>("textlanguage");
mManagerService.onDetectLanguage(mSessionId, request, callback);
@@ -214,9 +211,7 @@
Utils.checkMainThread();
try {
- request.setCallingPackageName(mPackageName);
- request.setUserId(mUserId);
- request.setUseDefaultTextClassifier(mUseDefault);
+ request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<ConversationActions> callback =
new BlockingCallback<>("conversation-actions");
mManagerService.onSuggestConversationActions(mSessionId, request, callback);
@@ -256,10 +251,8 @@
printWriter.println("SystemTextClassifier:");
printWriter.increaseIndent();
printWriter.printPair("mFallback", mFallback);
- printWriter.printPair("mPackageName", mPackageName);
printWriter.printPair("mSessionId", mSessionId);
- printWriter.printPair("mUserId", mUserId);
- printWriter.printPair("mUseDefault", mUseDefault);
+ printWriter.printPair("mSystemTcMetadata", mSystemTcMetadata);
printWriter.decreaseIndent();
printWriter.println();
}
@@ -275,7 +268,7 @@
@NonNull TextClassificationSessionId sessionId) {
mSessionId = Objects.requireNonNull(sessionId);
try {
- classificationContext.setUserId(mUserId);
+ classificationContext.setSystemTextClassifierMetadata(mSystemTcMetadata);
mManagerService.onCreateTextClassificationSession(classificationContext, mSessionId);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error starting a new classification session.", e);
diff --git a/core/java/android/os/incremental/IncrementalSignature.aidl b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl
similarity index 63%
rename from core/java/android/os/incremental/IncrementalSignature.aidl
rename to core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl
index 729e8e5..4d4e90a 100644
--- a/core/java/android/os/incremental/IncrementalSignature.aidl
+++ b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.aidl
@@ -14,18 +14,6 @@
* limitations under the License.
*/
-package android.os.incremental;
+package android.view.textclassifier;
-/** {@hide} */
-parcelable IncrementalSignature {
- /*
- * Stable AIDL doesn't support constants, but here's the possible values
- * const int HASH_ALGO_NONE = 0;
- * const int HASH_ALGO_SHA256 = 1;
- */
-
- int hashAlgorithm = 0;
- byte[] rootHash;
- byte[] additionalData;
- byte[] signature;
-}
+parcelable SystemTextClassifierMetadata;
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java
new file mode 100644
index 0000000..971e3e2
--- /dev/null
+++ b/core/java/android/view/textclassifier/SystemTextClassifierMetadata.java
@@ -0,0 +1,121 @@
+/*
+ * 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 android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * SystemTextClassifier specific information.
+ * <p>
+ * This contains information requires for the TextClassificationManagerService to process the
+ * requests from the application, e.g. user id, calling package name and etc. Centrialize the data
+ * into this class helps to extend the scalability if we want to add new fields.
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PACKAGE)
+public final class SystemTextClassifierMetadata implements Parcelable {
+
+ /* The name of the package that sent the TC request. */
+ @NonNull
+ private final String mCallingPackageName;
+ /* The id of the user that sent the TC request. */
+ @UserIdInt
+ private final int mUserId;
+ /* Whether to use the default text classifier to handle the request. */
+ private final boolean mUseDefaultTextClassifier;
+
+ public SystemTextClassifierMetadata(@NonNull String packageName, @UserIdInt int userId,
+ boolean useDefaultTextClassifier) {
+ Objects.requireNonNull(packageName);
+ mCallingPackageName = packageName;
+ mUserId = userId;
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns the id of the user that sent the TC request.
+ */
+ @UserIdInt
+ public int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * Returns the name of the package that sent the TC request.
+ * This returns {@code null} if no calling package name is set.
+ */
+ @NonNull
+ public String getCallingPackageName() {
+ return mCallingPackageName;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle TC request.
+ */
+ public boolean useDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.US,
+ "SystemTextClassifierMetadata {callingPackageName=%s, userId=%d, "
+ + "useDefaultTextClassifier=%b}",
+ mCallingPackageName, mUserId, mUseDefaultTextClassifier);
+ }
+
+ private static SystemTextClassifierMetadata readFromParcel(Parcel in) {
+ final String packageName = in.readString();
+ final int userId = in.readInt();
+ final boolean useDefaultTextClassifier = in.readBoolean();
+ return new SystemTextClassifierMetadata(packageName, userId, useDefaultTextClassifier);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mCallingPackageName);
+ dest.writeInt(mUserId);
+ dest.writeBoolean(mUseDefaultTextClassifier);
+ }
+
+ public static final @NonNull Creator<SystemTextClassifierMetadata> CREATOR =
+ new Creator<SystemTextClassifierMetadata>() {
+ @Override
+ public SystemTextClassifierMetadata createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public SystemTextClassifierMetadata[] newArray(int size) {
+ return new SystemTextClassifierMetadata[size];
+ }
+ };
+}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 00f762b..3aed32a 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -21,7 +21,6 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
@@ -36,7 +35,6 @@
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.View.OnClickListener;
@@ -46,8 +44,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.google.android.textclassifier.AnnotatorModel;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.ZonedDateTime;
@@ -329,9 +325,6 @@
@NonNull private List<RemoteAction> mActions = new ArrayList<>();
@NonNull private final Map<String, Float> mTypeScoreMap = new ArrayMap<>();
- @NonNull
- private final Map<String, AnnotatorModel.ClassificationResult> mClassificationResults =
- new ArrayMap<>();
@Nullable private String mText;
@Nullable private Drawable mLegacyIcon;
@Nullable private String mLegacyLabel;
@@ -364,36 +357,7 @@
public Builder setEntityType(
@NonNull @EntityType String type,
@FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
- setEntityType(type, confidenceScore, null);
- return this;
- }
-
- /**
- * @see #setEntityType(String, float)
- *
- * @hide
- */
- @NonNull
- public Builder setEntityType(AnnotatorModel.ClassificationResult classificationResult) {
- setEntityType(
- classificationResult.getCollection(),
- classificationResult.getScore(),
- classificationResult);
- return this;
- }
-
- /**
- * @see #setEntityType(String, float)
- *
- * @hide
- */
- @NonNull
- private Builder setEntityType(
- @NonNull @EntityType String type,
- @FloatRange(from = 0.0, to = 1.0) float confidenceScore,
- @Nullable AnnotatorModel.ClassificationResult classificationResult) {
mTypeScoreMap.put(type, confidenceScore);
- mClassificationResults.put(type, classificationResult);
return this;
}
@@ -519,25 +483,7 @@
EntityConfidence entityConfidence = new EntityConfidence(mTypeScoreMap);
return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
mLegacyOnClickListener, mActions, entityConfidence, mId,
- buildExtras(entityConfidence));
- }
-
- private Bundle buildExtras(EntityConfidence entityConfidence) {
- final Bundle extras = mExtras == null ? new Bundle() : mExtras;
- if (mActionIntents.stream().anyMatch(Objects::nonNull)) {
- ExtrasUtils.putActionsIntents(extras, mActionIntents);
- }
- if (mForeignLanguageExtra != null) {
- ExtrasUtils.putForeignLanguageExtra(extras, mForeignLanguageExtra);
- }
- List<String> sortedTypes = entityConfidence.getEntities();
- ArrayList<AnnotatorModel.ClassificationResult> sortedEntities = new ArrayList<>();
- for (String type : sortedTypes) {
- sortedEntities.add(mClassificationResults.get(type));
- }
- ExtrasUtils.putEntities(
- extras, sortedEntities.toArray(new AnnotatorModel.ClassificationResult[0]));
- return extras.isEmpty() ? Bundle.EMPTY : extras;
+ mExtras == null ? Bundle.EMPTY : mExtras);
}
}
@@ -552,10 +498,7 @@
@Nullable private final LocaleList mDefaultLocales;
@Nullable private final ZonedDateTime mReferenceTime;
@NonNull private final Bundle mExtras;
- @Nullable private String mCallingPackageName;
- @UserIdInt
- private int mUserId = UserHandle.USER_NULL;
- private boolean mUseDefaultTextClassifier;
+ @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
private Request(
CharSequence text,
@@ -616,62 +559,33 @@
}
/**
- * Sets the name of the package that is sending this request.
- * <p>
- * For SystemTextClassifier's use.
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void setCallingPackageName(@Nullable String callingPackageName) {
- mCallingPackageName = callingPackageName;
- }
-
- /**
* Returns the name of the package that sent this request.
* This returns {@code null} if no calling package name is set.
*/
@Nullable
public String getCallingPackageName() {
- return mCallingPackageName;
+ return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
}
/**
- * Sets the id of the user that sent this request.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- void setUserId(@UserIdInt int userId) {
- mUserId = userId;
- }
-
- /**
- * Returns the id of the user that sent this request.
- * @hide
- */
- @UserIdInt
- public int getUserId() {
- return mUserId;
- }
-
- /**
- * Sets whether to use the default text classifier to handle this request.
- * This will be ignored if it is not the system text classifier to handle this request.
+ * Sets the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
- mUseDefaultTextClassifier = useDefaultTextClassifier;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setSystemTextClassifierMetadata(
+ @Nullable SystemTextClassifierMetadata systemTcMetadata) {
+ mSystemTcMetadata = systemTcMetadata;
}
/**
- * Returns whether to use the default text classifier to handle this request. This
- * will be ignored if it is not the system text classifier to handle this request.
+ * Returns the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- public boolean getUseDefaultTextClassifier() {
- return mUseDefaultTextClassifier;
+ @Nullable
+ public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+ return mSystemTcMetadata;
}
/**
@@ -773,10 +687,8 @@
dest.writeInt(mEndIndex);
dest.writeParcelable(mDefaultLocales, flags);
dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
- dest.writeString(mCallingPackageName);
- dest.writeInt(mUserId);
dest.writeBundle(mExtras);
- dest.writeBoolean(mUseDefaultTextClassifier);
+ dest.writeParcelable(mSystemTcMetadata, flags);
}
private static Request readFromParcel(Parcel in) {
@@ -787,16 +699,12 @@
final String referenceTimeString = in.readString();
final ZonedDateTime referenceTime = referenceTimeString == null
? null : ZonedDateTime.parse(referenceTimeString);
- final String callingPackageName = in.readString();
- final int userId = in.readInt();
final Bundle extras = in.readBundle();
- final boolean useDefaultTextClassifier = in.readBoolean();
+ final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
final Request request = new Request(text, startIndex, endIndex,
defaultLocales, referenceTime, extras);
- request.setCallingPackageName(callingPackageName);
- request.setUserId(userId);
- request.setUseDefaultTextClassifier(useDefaultTextClassifier);
+ request.setSystemTextClassifierMetadata(systemTcMetadata);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 3d5ac58..adb6fea 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -17,16 +17,11 @@
package android.view.textclassifier;
import android.annotation.Nullable;
-import android.content.Context;
import android.provider.DeviceConfig;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
/**
* TextClassifier specific settings.
*
@@ -41,8 +36,6 @@
*/
// TODO: Rename to TextClassifierSettings.
public final class TextClassificationConstants {
- private static final String DELIMITER = ":";
-
/**
* Whether the smart linkify feature is enabled.
*/
@@ -60,7 +53,6 @@
* Enable smart selection without a visible UI changes.
*/
private static final String MODEL_DARK_LAUNCH_ENABLED = "model_dark_launch_enabled";
-
/**
* Whether the smart selection feature is enabled.
*/
@@ -75,88 +67,10 @@
private static final String SMART_SELECT_ANIMATION_ENABLED =
"smart_select_animation_enabled";
/**
- * Max length of text that suggestSelection can accept.
- */
- @VisibleForTesting
- static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
- "suggest_selection_max_range_length";
- /**
- * Max length of text that classifyText can accept.
- */
- private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH = "classify_text_max_range_length";
- /**
* Max length of text that generateLinks can accept.
*/
- private static final String GENERATE_LINKS_MAX_TEXT_LENGTH = "generate_links_max_text_length";
- /**
- * Sampling rate for generateLinks logging.
- */
- private static final String GENERATE_LINKS_LOG_SAMPLE_RATE =
- "generate_links_log_sample_rate";
- /**
- * A colon(:) separated string that specifies the default entities types for
- * generateLinks when hint is not given.
- */
@VisibleForTesting
- static final String ENTITY_LIST_DEFAULT = "entity_list_default";
- /**
- * A colon(:) separated string that specifies the default entities types for
- * generateLinks when the text is in a not editable UI widget.
- */
- private static final String ENTITY_LIST_NOT_EDITABLE = "entity_list_not_editable";
- /**
- * A colon(:) separated string that specifies the default entities types for
- * generateLinks when the text is in an editable UI widget.
- */
- private static final String ENTITY_LIST_EDITABLE = "entity_list_editable";
- /**
- * A colon(:) separated string that specifies the default action types for
- * suggestConversationActions when the suggestions are used in an app.
- */
- private static final String IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT =
- "in_app_conversation_action_types_default";
- /**
- * A colon(:) separated string that specifies the default action types for
- * suggestConversationActions when the suggestions are used in a notification.
- */
- private static final String NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT =
- "notification_conversation_action_types_default";
- /**
- * Threshold to accept a suggested language from LangID model.
- */
- @VisibleForTesting
- static final String LANG_ID_THRESHOLD_OVERRIDE = "lang_id_threshold_override";
- /**
- * Whether to enable {@link android.view.textclassifier.TemplateIntentFactory}.
- */
- private static final String TEMPLATE_INTENT_FACTORY_ENABLED = "template_intent_factory_enabled";
- /**
- * Whether to enable "translate" action in classifyText.
- */
- private static final String TRANSLATE_IN_CLASSIFICATION_ENABLED =
- "translate_in_classification_enabled";
- /**
- * Whether to detect the languages of the text in request by using langId for the native
- * model.
- */
- private static final String DETECT_LANGUAGES_FROM_TEXT_ENABLED =
- "detect_languages_from_text_enabled";
- /**
- * A colon(:) separated string that specifies the configuration to use when including
- * surrounding context text in language detection queries.
- * <p>
- * Format= minimumTextSize<int>:penalizeRatio<float>:textScoreRatio<float>
- * <p>
- * e.g. 20:1.0:0.4
- * <p>
- * Accept all text lengths with minimumTextSize=0
- * <p>
- * Reject all text less than minimumTextSize with penalizeRatio=0
- * @see {@code TextClassifierImpl#detectLanguages(String, int, int)} for reference.
- */
- @VisibleForTesting
- static final String LANG_ID_CONTEXT_SETTINGS = "lang_id_context_settings";
-
+ static final String GENERATE_LINKS_MAX_TEXT_LENGTH = "generate_links_max_text_length";
/**
* The TextClassifierService which would like to use. Example of setting the package:
* <pre>
@@ -168,16 +82,6 @@
static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
"textclassifier_service_package_override";
- /**
- * Whether to use the default system text classifier as the default text classifier
- * implementation. The local text classifier is used if it is {@code false}.
- *
- * @see android.service.textclassifier.TextClassifierService#getDefaultTextClassifierImplementation(Context)
- */
- // TODO: Once the system health experiment is done, remove this together with local TC.
- private static final String USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL =
- "use_default_system_text_classifier_as_default_impl";
-
private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -186,42 +90,7 @@
private static final boolean SMART_TEXT_SHARE_ENABLED_DEFAULT = true;
private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
- private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
- private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
- private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
- private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList(
- TextClassifier.TYPE_ADDRESS,
- TextClassifier.TYPE_EMAIL,
- TextClassifier.TYPE_PHONE,
- TextClassifier.TYPE_URL,
- TextClassifier.TYPE_DATE,
- TextClassifier.TYPE_DATE_TIME,
- TextClassifier.TYPE_FLIGHT_NUMBER);
- private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList(
- ConversationAction.TYPE_TEXT_REPLY,
- ConversationAction.TYPE_CREATE_REMINDER,
- ConversationAction.TYPE_CALL_PHONE,
- ConversationAction.TYPE_OPEN_URL,
- ConversationAction.TYPE_SEND_EMAIL,
- ConversationAction.TYPE_SEND_SMS,
- ConversationAction.TYPE_TRACK_FLIGHT,
- ConversationAction.TYPE_VIEW_CALENDAR,
- ConversationAction.TYPE_VIEW_MAP,
- ConversationAction.TYPE_ADD_CONTACT,
- ConversationAction.TYPE_COPY);
- /**
- * < 0 : Not set. Use value from LangId model.
- * 0 - 1: Override value in LangId model.
- *
- * @see EntityConfidence
- */
- private static final float LANG_ID_THRESHOLD_OVERRIDE_DEFAULT = -1f;
- private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true;
- private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true;
- private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
- private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[]{20f, 1.0f, 0.4f};
- private static final boolean USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT = true;
@Nullable
public String getTextClassifierServicePackageOverride() {
@@ -266,119 +135,20 @@
SMART_SELECT_ANIMATION_ENABLED, SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
}
- public int getSuggestSelectionMaxRangeLength() {
- return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- SUGGEST_SELECTION_MAX_RANGE_LENGTH, SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
- }
-
- public int getClassifyTextMaxRangeLength() {
- return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- CLASSIFY_TEXT_MAX_RANGE_LENGTH, CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
- }
-
public int getGenerateLinksMaxTextLength() {
return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
GENERATE_LINKS_MAX_TEXT_LENGTH, GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
}
- public int getGenerateLinksLogSampleRate() {
- return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- GENERATE_LINKS_LOG_SAMPLE_RATE, GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
- }
-
- public List<String> getEntityListDefault() {
- return getDeviceConfigStringList(ENTITY_LIST_DEFAULT, ENTITY_LIST_DEFAULT_VALUE);
- }
-
- public List<String> getEntityListNotEditable() {
- return getDeviceConfigStringList(ENTITY_LIST_NOT_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
- }
-
- public List<String> getEntityListEditable() {
- return getDeviceConfigStringList(ENTITY_LIST_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
- }
-
- public List<String> getInAppConversationActionTypes() {
- return getDeviceConfigStringList(
- IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
- CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
- }
-
- public List<String> getNotificationConversationActionTypes() {
- return getDeviceConfigStringList(
- NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
- CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
- }
-
- public float getLangIdThresholdOverride() {
- return DeviceConfig.getFloat(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- LANG_ID_THRESHOLD_OVERRIDE,
- LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
- }
-
- public boolean isTemplateIntentFactoryEnabled() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TEMPLATE_INTENT_FACTORY_ENABLED,
- TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
- }
-
- public boolean isTranslateInClassificationEnabled() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TRANSLATE_IN_CLASSIFICATION_ENABLED,
- TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
- }
-
- public boolean isDetectLanguagesFromTextEnabled() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- DETECT_LANGUAGES_FROM_TEXT_ENABLED,
- DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
- }
-
- public float[] getLangIdContextSettings() {
- return getDeviceConfigFloatArray(
- LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT);
- }
-
- public boolean getUseDefaultTextClassifierAsDefaultImplementation() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL,
- USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT);
- }
-
void dump(IndentingPrintWriter pw) {
pw.println("TextClassificationConstants:");
pw.increaseIndent();
- pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength())
- .println();
- pw.printPair("detect_languages_from_text_enabled", isDetectLanguagesFromTextEnabled())
- .println();
- pw.printPair("entity_list_default", getEntityListDefault())
- .println();
- pw.printPair("entity_list_editable", getEntityListEditable())
- .println();
- pw.printPair("entity_list_not_editable", getEntityListNotEditable())
- .println();
- pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate())
- .println();
pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength())
.println();
- pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes())
- .println();
- pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings()))
- .println();
- pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride())
- .println();
pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled())
.println();
pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled())
.println();
- pw.printPair("notification_conversation_action_types_default",
- getNotificationConversationActionTypes()).println();
pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled())
.println();
pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled())
@@ -387,57 +157,10 @@
.println();
pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled())
.println();
- pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength())
- .println();
pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled())
.println();
- pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled())
- .println();
- pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled())
- .println();
pw.printPair("textclassifier_service_package_override",
getTextClassifierServicePackageOverride()).println();
- pw.printPair("use_default_system_text_classifier_as_default_impl",
- getUseDefaultTextClassifierAsDefaultImplementation()).println();
pw.decreaseIndent();
}
-
- private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
- return parse(
- DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
- defaultValue);
- }
-
- private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
- return parse(
- DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
- defaultValue);
- }
-
- private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
- if (listStr != null) {
- return Collections.unmodifiableList(Arrays.asList(listStr.split(DELIMITER)));
- }
- return defaultValue;
- }
-
- private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
- if (arrayStr != null) {
- final String[] split = arrayStr.split(DELIMITER);
- if (split.length != defaultValue.length) {
- return defaultValue;
- }
- final float[] result = new float[split.length];
- for (int i = 0; i < split.length; i++) {
- try {
- result[i] = Float.parseFloat(split[i]);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
- return result;
- } else {
- return defaultValue;
- }
- }
}
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index d58d175..5d5683f 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -18,10 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.view.textclassifier.TextClassifier.WidgetType;
import java.util.Locale;
@@ -33,12 +31,10 @@
*/
public final class TextClassificationContext implements Parcelable {
- private final String mPackageName;
+ private String mPackageName;
private final String mWidgetType;
@Nullable private final String mWidgetVersion;
- @UserIdInt
- private int mUserId = UserHandle.USER_NULL;
- private boolean mUseDefaultTextClassifier;
+ private SystemTextClassifierMetadata mSystemTcMetadata;
private TextClassificationContext(
String packageName,
@@ -50,7 +46,7 @@
}
/**
- * Returns the package name for the calling package.
+ * Returns the package name of the app that this context originated in.
*/
@NonNull
public String getPackageName() {
@@ -58,42 +54,22 @@
}
/**
- * Sets the id of this context's user.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- void setUserId(@UserIdInt int userId) {
- mUserId = userId;
- }
-
- /**
- * Returns the id of this context's user.
- * @hide
- */
- @UserIdInt
- public int getUserId() {
- return mUserId;
- }
-
- /**
- * Sets whether to use the default text classifier to handle this request.
- * This will be ignored if it is not the system text classifier to handle this request.
+ * Sets the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
- mUseDefaultTextClassifier = useDefaultTextClassifier;
+ void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) {
+ mSystemTcMetadata = systemTcMetadata;
}
/**
- * Returns whether to use the default text classifier to handle this request. This
- * will be ignored if it is not the system text classifier to handle this request.
+ * Returns the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- public boolean getUseDefaultTextClassifier() {
- return mUseDefaultTextClassifier;
+ @Nullable
+ public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+ return mSystemTcMetadata;
}
/**
@@ -118,8 +94,8 @@
@Override
public String toString() {
return String.format(Locale.US, "TextClassificationContext{"
- + "packageName=%s, widgetType=%s, widgetVersion=%s, userId=%d}",
- mPackageName, mWidgetType, mWidgetVersion, mUserId);
+ + "packageName=%s, widgetType=%s, widgetVersion=%s, systemTcMetadata=%s}",
+ mPackageName, mWidgetType, mWidgetVersion, mSystemTcMetadata);
}
/**
@@ -176,16 +152,14 @@
parcel.writeString(mPackageName);
parcel.writeString(mWidgetType);
parcel.writeString(mWidgetVersion);
- parcel.writeInt(mUserId);
- parcel.writeBoolean(mUseDefaultTextClassifier);
+ parcel.writeParcelable(mSystemTcMetadata, flags);
}
private TextClassificationContext(Parcel in) {
mPackageName = in.readString();
mWidgetType = in.readString();
mWidgetVersion = in.readString();
- mUserId = in.readInt();
- mUseDefaultTextClassifier = in.readBoolean();
+ mSystemTcMetadata = in.readParcelable(null);
}
public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR =
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index dfbec9b..fa4f7d6 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -19,21 +19,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
-import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.ServiceManager;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
import android.view.textclassifier.TextClassifier.TextClassifierType;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
-import java.lang.ref.WeakReference;
import java.util.Objects;
-import java.util.Set;
/**
* Interface to the text classification service.
@@ -41,7 +35,7 @@
@SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
public final class TextClassificationManager {
- private static final String LOG_TAG = "TextClassificationManager";
+ private static final String LOG_TAG = TextClassifier.LOG_TAG;
private static final TextClassificationConstants sDefaultSettings =
new TextClassificationConstants();
@@ -52,15 +46,11 @@
classificationContext, getTextClassifier());
private final Context mContext;
- private final SettingsObserver mSettingsObserver;
@GuardedBy("mLock")
@Nullable
private TextClassifier mCustomTextClassifier;
@GuardedBy("mLock")
- @Nullable
- private TextClassifier mLocalTextClassifier;
- @GuardedBy("mLock")
private TextClassificationSessionFactory mSessionFactory;
@GuardedBy("mLock")
private TextClassificationConstants mSettings;
@@ -69,7 +59,6 @@
public TextClassificationManager(Context context) {
mContext = Objects.requireNonNull(context);
mSessionFactory = mDefaultSessionFactory;
- mSettingsObserver = new SettingsObserver(this);
}
/**
@@ -112,7 +101,7 @@
*
* @see TextClassifier#LOCAL
* @see TextClassifier#SYSTEM
- * @see TextClassifier#DEFAULT_SERVICE
+ * @see TextClassifier#DEFAULT_SYSTEM
* @hide
*/
@UnsupportedAppUsage
@@ -189,28 +178,17 @@
}
}
- @Override
- protected void finalize() throws Throwable {
- try {
- // Note that fields could be null if the constructor threw.
- if (mSettingsObserver != null) {
- DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver);
- }
- } finally {
- super.finalize();
- }
- }
-
/** @hide */
private TextClassifier getSystemTextClassifier(@TextClassifierType int type) {
synchronized (mLock) {
if (getSettings().isSystemTextClassifierEnabled()) {
try {
- Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = " + type);
+ Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = "
+ + TextClassifier.typeToString(type));
return new SystemTextClassifier(
mContext,
getSettings(),
- /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE);
+ /* useDefault= */ type == TextClassifier.DEFAULT_SYSTEM);
} catch (ServiceManager.ServiceNotFoundException e) {
Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
}
@@ -224,49 +202,13 @@
*/
@NonNull
private TextClassifier getLocalTextClassifier() {
- synchronized (mLock) {
- if (mLocalTextClassifier == null) {
- if (getSettings().isLocalTextClassifierEnabled()) {
- mLocalTextClassifier =
- new TextClassifierImpl(mContext, getSettings(), TextClassifier.NO_OP);
- } else {
- Log.d(LOG_TAG, "Local TextClassifier disabled");
- mLocalTextClassifier = TextClassifier.NO_OP;
- }
- }
- return mLocalTextClassifier;
- }
- }
-
- /** @hide */
- @VisibleForTesting
- public void invalidateForTesting() {
- invalidate();
- }
-
- private void invalidate() {
- synchronized (mLock) {
- mSettings = null;
- invalidateTextClassifiers();
- }
- }
-
- private void invalidateTextClassifiers() {
- synchronized (mLock) {
- mLocalTextClassifier = null;
- }
- }
-
- Context getApplicationContext() {
- return mContext.getApplicationContext() != null
- ? mContext.getApplicationContext()
- : mContext;
+ Log.d(LOG_TAG, "Local text-classifier not supported. Returning a no-op text-classifier.");
+ return TextClassifier.NO_OP;
}
/** @hide **/
public void dump(IndentingPrintWriter pw) {
- getLocalTextClassifier().dump(pw);
- getSystemTextClassifier(TextClassifier.DEFAULT_SERVICE).dump(pw);
+ getSystemTextClassifier(TextClassifier.DEFAULT_SYSTEM).dump(pw);
getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw);
getSettings().dump(pw);
}
@@ -283,31 +225,4 @@
return sDefaultSettings;
}
}
-
- private static final class SettingsObserver implements
- DeviceConfig.OnPropertiesChangedListener {
-
- private final WeakReference<TextClassificationManager> mTcm;
-
- SettingsObserver(TextClassificationManager tcm) {
- mTcm = new WeakReference<>(tcm);
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- ActivityThread.currentApplication().getMainExecutor(),
- this);
- }
-
- @Override
- public void onPropertiesChanged(Properties properties) {
- final TextClassificationManager tcm = mTcm.get();
- if (tcm != null) {
- final Set<String> keys = properties.getKeyset();
- if (keys.contains(TextClassificationConstants.SYSTEM_TEXT_CLASSIFIER_ENABLED)
- || keys.contains(
- TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED)) {
- tcm.invalidateTextClassifiers();
- }
- }
- }
- }
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 2cc226d..6d5077a 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -61,19 +61,32 @@
public interface TextClassifier {
/** @hide */
- String DEFAULT_LOG_TAG = "androidtc";
+ String LOG_TAG = "androidtc";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SERVICE})
+ @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SYSTEM})
@interface TextClassifierType {} // TODO: Expose as system APIs.
/** Specifies a TextClassifier that runs locally in the app's process. @hide */
int LOCAL = 0;
/** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
int SYSTEM = 1;
/** Specifies the default TextClassifier that runs in the system process. @hide */
- int DEFAULT_SERVICE = 2;
+ int DEFAULT_SYSTEM = 2;
+
+ /** @hide */
+ static String typeToString(@TextClassifierType int type) {
+ switch (type) {
+ case LOCAL:
+ return "Local";
+ case SYSTEM:
+ return "System";
+ case DEFAULT_SYSTEM:
+ return "Default system";
+ }
+ return "Unknown";
+ }
/** The TextClassifier failed to run. */
String TYPE_UNKNOWN = "";
@@ -776,7 +789,7 @@
static void checkMainThread() {
if (Looper.myLooper() == Looper.getMainLooper()) {
- Log.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
+ Log.w(LOG_TAG, "TextClassifier called on main thread");
}
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
deleted file mode 100644
index 8162699..0000000
--- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SESSION_ID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_VERSION;
-
-import android.metrics.LogMaker;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import java.util.Objects;
-
-
-/**
- * Log {@link TextClassifierEvent} by using Tron, only support language detection and
- * conversation actions.
- *
- * @hide
- */
-public final class TextClassifierEventTronLogger {
-
- private static final String TAG = "TCEventTronLogger";
-
- private final MetricsLogger mMetricsLogger;
-
- public TextClassifierEventTronLogger() {
- this(new MetricsLogger());
- }
-
- @VisibleForTesting
- public TextClassifierEventTronLogger(MetricsLogger metricsLogger) {
- mMetricsLogger = Objects.requireNonNull(metricsLogger);
- }
-
- /** Emits a text classifier event to the logs. */
- public void writeEvent(TextClassifierEvent event) {
- Objects.requireNonNull(event);
-
- int category = getCategory(event);
- if (category == -1) {
- Log.w(TAG, "Unknown category: " + event.getEventCategory());
- return;
- }
- final LogMaker log = new LogMaker(category)
- .setSubtype(getLogType(event))
- .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
- .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event));
- if (event.getScores().length >= 1) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScores()[0]);
- }
- String[] entityTypes = event.getEntityTypes();
- // The old logger does not support a field of list type, and thus workaround by store them
- // in three separate fields. This is not an issue with the new logger.
- if (entityTypes.length >= 1) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE, entityTypes[0]);
- }
- if (entityTypes.length >= 2) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE, entityTypes[1]);
- }
- if (entityTypes.length >= 3) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE, entityTypes[2]);
- }
- TextClassificationContext eventContext = event.getEventContext();
- if (eventContext != null) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE, eventContext.getWidgetType());
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION,
- eventContext.getWidgetVersion());
- log.setPackageName(eventContext.getPackageName());
- }
- mMetricsLogger.write(log);
- debugLog(log);
- }
-
- private static String getModelName(TextClassifierEvent event) {
- if (event.getModelName() != null) {
- return event.getModelName();
- }
- return SelectionSessionLogger.SignatureParser.getModelName(event.getResultId());
- }
-
- private static int getCategory(TextClassifierEvent event) {
- switch (event.getEventCategory()) {
- case TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS:
- return MetricsEvent.CONVERSATION_ACTIONS;
- case TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION:
- return MetricsEvent.LANGUAGE_DETECTION;
- }
- return -1;
- }
-
- private static int getLogType(TextClassifierEvent event) {
- switch (event.getEventType()) {
- case TextClassifierEvent.TYPE_SMART_ACTION:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
- case TextClassifierEvent.TYPE_ACTIONS_SHOWN:
- return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
- case TextClassifierEvent.TYPE_MANUAL_REPLY:
- return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
- case TextClassifierEvent.TYPE_ACTIONS_GENERATED:
- return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED;
- default:
- return MetricsEvent.VIEW_UNKNOWN;
- }
- }
-
- private String toCategoryName(int category) {
- switch (category) {
- case MetricsEvent.CONVERSATION_ACTIONS:
- return "conversation_actions";
- case MetricsEvent.LANGUAGE_DETECTION:
- return "language_detection";
- }
- return "unknown";
- }
-
- private String toEventName(int logType) {
- switch (logType) {
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
- return "smart_share";
- case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN:
- return "actions_shown";
- case MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY:
- return "manual_reply";
- case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED:
- return "actions_generated";
- }
- return "unknown";
- }
-
- private void debugLog(LogMaker log) {
- if (!Log.ENABLE_FULL_LOGGING) {
- return;
- }
- final String id = String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID));
- final String categoryName = toCategoryName(log.getCategory());
- final String eventName = toEventName(log.getSubtype());
- final String widgetType =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE));
- final String widgetVersion =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION));
- final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
- final String firstEntityType =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE));
- final String secondEntityType =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE));
- final String thirdEntityType =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE));
- final String score =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE));
-
- StringBuilder builder = new StringBuilder();
- builder.append("writeEvent: ");
- builder.append("id=").append(id);
- builder.append(", category=").append(categoryName);
- builder.append(", eventName=").append(eventName);
- builder.append(", widgetType=").append(widgetType);
- builder.append(", widgetVersion=").append(widgetVersion);
- builder.append(", model=").append(model);
- builder.append(", firstEntityType=").append(firstEntityType);
- builder.append(", secondEntityType=").append(secondEntityType);
- builder.append(", thirdEntityType=").append(thirdEntityType);
- builder.append(", score=").append(score);
-
- Log.v(TAG, builder.toString());
- }
-}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
deleted file mode 100644
index d7149ee..0000000
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ /dev/null
@@ -1,911 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.app.RemoteAction;
-import android.content.Context;
-import android.content.Intent;
-import android.icu.util.ULocale;
-import android.os.Bundle;
-import android.os.LocaleList;
-import android.os.ParcelFileDescriptor;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Pair;
-import android.view.textclassifier.ActionsModelParamsSupplier.ActionsModelParams;
-import android.view.textclassifier.intent.ClassificationIntentFactory;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.LegacyClassificationIntentFactory;
-import android.view.textclassifier.intent.TemplateClassificationIntentFactory;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.LangIdModel;
-import com.google.android.textclassifier.LangIdModel.LanguageResult;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.time.Instant;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Supplier;
-
-/**
- * Default implementation of the {@link TextClassifier} interface.
- *
- * <p>This class uses machine learning to recognize entities in text.
- * Unless otherwise stated, methods of this class are blocking operations and should most
- * likely not be called on the UI thread.
- *
- * @hide
- */
-public final class TextClassifierImpl implements TextClassifier {
-
- private static final String LOG_TAG = DEFAULT_LOG_TAG;
-
- private static final boolean DEBUG = false;
-
- private static final File FACTORY_MODEL_DIR = new File("/etc/textclassifier/");
- // Annotator
- private static final String ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX =
- "textclassifier\\.(.*)\\.model";
- private static final File ANNOTATOR_UPDATED_MODEL_FILE =
- new File("/data/misc/textclassifier/textclassifier.model");
-
- // LangID
- private static final String LANG_ID_FACTORY_MODEL_FILENAME_REGEX = "lang_id.model";
- private static final File UPDATED_LANG_ID_MODEL_FILE =
- new File("/data/misc/textclassifier/lang_id.model");
-
- // Actions
- private static final String ACTIONS_FACTORY_MODEL_FILENAME_REGEX =
- "actions_suggestions\\.(.*)\\.model";
- private static final File UPDATED_ACTIONS_MODEL =
- new File("/data/misc/textclassifier/actions_suggestions.model");
-
- private final Context mContext;
- private final TextClassifier mFallback;
- private final GenerateLinksLogger mGenerateLinksLogger;
-
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private ModelFileManager.ModelFile mAnnotatorModelInUse;
- @GuardedBy("mLock")
- private AnnotatorModel mAnnotatorImpl;
-
- @GuardedBy("mLock")
- private ModelFileManager.ModelFile mLangIdModelInUse;
- @GuardedBy("mLock")
- private LangIdModel mLangIdImpl;
-
- @GuardedBy("mLock")
- private ModelFileManager.ModelFile mActionModelInUse;
- @GuardedBy("mLock")
- private ActionsSuggestionsModel mActionsImpl;
-
- private final SelectionSessionLogger mSessionLogger = new SelectionSessionLogger();
- private final TextClassifierEventTronLogger mTextClassifierEventTronLogger =
- new TextClassifierEventTronLogger();
-
- private final TextClassificationConstants mSettings;
-
- private final ModelFileManager mAnnotatorModelFileManager;
- private final ModelFileManager mLangIdModelFileManager;
- private final ModelFileManager mActionsModelFileManager;
-
- private final ClassificationIntentFactory mClassificationIntentFactory;
- private final TemplateIntentFactory mTemplateIntentFactory;
- private final Supplier<ActionsModelParams> mActionsModelParamsSupplier;
-
- public TextClassifierImpl(
- Context context, TextClassificationConstants settings, TextClassifier fallback) {
- mContext = Objects.requireNonNull(context);
- mFallback = Objects.requireNonNull(fallback);
- mSettings = Objects.requireNonNull(settings);
- mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
- mAnnotatorModelFileManager = new ModelFileManager(
- new ModelFileManager.ModelFileSupplierImpl(
- FACTORY_MODEL_DIR,
- ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX,
- ANNOTATOR_UPDATED_MODEL_FILE,
- AnnotatorModel::getVersion,
- AnnotatorModel::getLocales));
- mLangIdModelFileManager = new ModelFileManager(
- new ModelFileManager.ModelFileSupplierImpl(
- FACTORY_MODEL_DIR,
- LANG_ID_FACTORY_MODEL_FILENAME_REGEX,
- UPDATED_LANG_ID_MODEL_FILE,
- LangIdModel::getVersion,
- fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT));
- mActionsModelFileManager = new ModelFileManager(
- new ModelFileManager.ModelFileSupplierImpl(
- FACTORY_MODEL_DIR,
- ACTIONS_FACTORY_MODEL_FILENAME_REGEX,
- UPDATED_ACTIONS_MODEL,
- ActionsSuggestionsModel::getVersion,
- ActionsSuggestionsModel::getLocales));
-
- mTemplateIntentFactory = new TemplateIntentFactory();
- mClassificationIntentFactory = mSettings.isTemplateIntentFactoryEnabled()
- ? new TemplateClassificationIntentFactory(
- mTemplateIntentFactory, new LegacyClassificationIntentFactory())
- : new LegacyClassificationIntentFactory();
- mActionsModelParamsSupplier = new ActionsModelParamsSupplier(mContext,
- () -> {
- synchronized (mLock) {
- // Clear mActionsImpl here, so that we will create a new
- // ActionsSuggestionsModel object with the new flag in the next request.
- mActionsImpl = null;
- mActionModelInUse = null;
- }
- });
- }
-
- public TextClassifierImpl(Context context, TextClassificationConstants settings) {
- this(context, settings, TextClassifier.NO_OP);
- }
-
- /** @inheritDoc */
- @Override
- @WorkerThread
- public TextSelection suggestSelection(TextSelection.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- try {
- final int rangeLength = request.getEndIndex() - request.getStartIndex();
- final String string = request.getText().toString();
- if (string.length() > 0
- && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
- final String localesString = concatenateLocales(request.getDefaultLocales());
- final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
- final ZonedDateTime refTime = ZonedDateTime.now();
- final AnnotatorModel annotatorImpl = getAnnotatorImpl(request.getDefaultLocales());
- final int start;
- final int end;
- if (mSettings.isModelDarkLaunchEnabled() && !request.isDarkLaunchAllowed()) {
- start = request.getStartIndex();
- end = request.getEndIndex();
- } else {
- final int[] startEnd = annotatorImpl.suggestSelection(
- string, request.getStartIndex(), request.getEndIndex(),
- new AnnotatorModel.SelectionOptions(localesString, detectLanguageTags));
- start = startEnd[0];
- end = startEnd[1];
- }
- if (start < end
- && start >= 0 && end <= string.length()
- && start <= request.getStartIndex() && end >= request.getEndIndex()) {
- final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
- final AnnotatorModel.ClassificationResult[] results =
- annotatorImpl.classifyText(
- string, start, end,
- new AnnotatorModel.ClassificationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- localesString,
- detectLanguageTags),
- // Passing null here to suppress intent generation
- // TODO: Use an explicit flag to suppress it.
- /* appContext */ null,
- /* deviceLocales */null);
- final int size = results.length;
- for (int i = 0; i < size; i++) {
- tsBuilder.setEntityType(results[i].getCollection(), results[i].getScore());
- }
- return tsBuilder.setId(createId(
- string, request.getStartIndex(), request.getEndIndex()))
- .build();
- } else {
- // We can not trust the result. Log the issue and ignore the result.
- Log.d(LOG_TAG, "Got bad indices for input text. Ignoring result.");
- }
- }
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG,
- "Error suggesting selection for text. No changes to selection suggested.",
- t);
- }
- // Getting here means something went wrong, return a NO_OP result.
- return mFallback.suggestSelection(request);
- }
-
- /** @inheritDoc */
- @Override
- @WorkerThread
- public TextClassification classifyText(TextClassification.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- try {
- final int rangeLength = request.getEndIndex() - request.getStartIndex();
- final String string = request.getText().toString();
- if (string.length() > 0 && rangeLength <= mSettings.getClassifyTextMaxRangeLength()) {
- final String localesString = concatenateLocales(request.getDefaultLocales());
- final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
- final ZonedDateTime refTime = request.getReferenceTime() != null
- ? request.getReferenceTime() : ZonedDateTime.now();
- final AnnotatorModel.ClassificationResult[] results =
- getAnnotatorImpl(request.getDefaultLocales())
- .classifyText(
- string, request.getStartIndex(), request.getEndIndex(),
- new AnnotatorModel.ClassificationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- localesString,
- detectLanguageTags),
- mContext,
- getResourceLocalesString()
- );
- if (results.length > 0) {
- return createClassificationResult(
- results, string,
- request.getStartIndex(), request.getEndIndex(), refTime.toInstant());
- }
- }
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error getting text classification info.", t);
- }
- // Getting here means something went wrong, return a NO_OP result.
- return mFallback.classifyText(request);
- }
-
- /** @inheritDoc */
- @Override
- @WorkerThread
- public TextLinks generateLinks(@NonNull TextLinks.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) {
- return mFallback.generateLinks(request);
- }
-
- if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
- return Utils.generateLegacyLinks(request);
- }
-
- final String textString = request.getText().toString();
- final TextLinks.Builder builder = new TextLinks.Builder(textString);
-
- try {
- final long startTimeMs = System.currentTimeMillis();
- final ZonedDateTime refTime = ZonedDateTime.now();
- final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
- ? request.getEntityConfig().resolveEntityListModifications(
- getEntitiesForHints(request.getEntityConfig().getHints()))
- : mSettings.getEntityListDefault();
- final String localesString = concatenateLocales(request.getDefaultLocales());
- final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
- final AnnotatorModel annotatorImpl =
- getAnnotatorImpl(request.getDefaultLocales());
- final boolean isSerializedEntityDataEnabled =
- ExtrasUtils.isSerializedEntityDataEnabled(request);
- final AnnotatorModel.AnnotatedSpan[] annotations =
- annotatorImpl.annotate(
- textString,
- new AnnotatorModel.AnnotationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- localesString,
- detectLanguageTags,
- entitiesToIdentify,
- AnnotatorModel.AnnotationUsecase.SMART.getValue(),
- isSerializedEntityDataEnabled));
- for (AnnotatorModel.AnnotatedSpan span : annotations) {
- final AnnotatorModel.ClassificationResult[] results =
- span.getClassification();
- if (results.length == 0
- || !entitiesToIdentify.contains(results[0].getCollection())) {
- continue;
- }
- final Map<String, Float> entityScores = new ArrayMap<>();
- for (int i = 0; i < results.length; i++) {
- entityScores.put(results[i].getCollection(), results[i].getScore());
- }
- Bundle extras = new Bundle();
- if (isSerializedEntityDataEnabled) {
- ExtrasUtils.putEntities(extras, results);
- }
- builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores, extras);
- }
- final TextLinks links = builder.build();
- final long endTimeMs = System.currentTimeMillis();
- final String callingPackageName = request.getCallingPackageName() == null
- ? mContext.getPackageName() // local (in process) TC.
- : request.getCallingPackageName();
- mGenerateLinksLogger.logGenerateLinks(
- request.getText(), links, callingPackageName, endTimeMs - startTimeMs);
- return links;
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error getting links info.", t);
- }
- return mFallback.generateLinks(request);
- }
-
- /** @inheritDoc */
- @Override
- public int getMaxGenerateLinksTextLength() {
- return mSettings.getGenerateLinksMaxTextLength();
- }
-
- private Collection<String> getEntitiesForHints(Collection<String> hints) {
- final boolean editable = hints.contains(HINT_TEXT_IS_EDITABLE);
- final boolean notEditable = hints.contains(HINT_TEXT_IS_NOT_EDITABLE);
-
- // Use the default if there is no hint, or conflicting ones.
- final boolean useDefault = editable == notEditable;
- if (useDefault) {
- return mSettings.getEntityListDefault();
- } else if (editable) {
- return mSettings.getEntityListEditable();
- } else { // notEditable
- return mSettings.getEntityListNotEditable();
- }
- }
-
- /** @inheritDoc */
- @Override
- public void onSelectionEvent(SelectionEvent event) {
- mSessionLogger.writeEvent(event);
- }
-
- @Override
- public void onTextClassifierEvent(TextClassifierEvent event) {
- if (DEBUG) {
- Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
- }
- try {
- final SelectionEvent selEvent = event.toSelectionEvent();
- if (selEvent != null) {
- mSessionLogger.writeEvent(selEvent);
- } else {
- mTextClassifierEventTronLogger.writeEvent(event);
- }
- } catch (Exception e) {
- Log.e(LOG_TAG, "Error writing event", e);
- }
- }
-
- /** @inheritDoc */
- @Override
- public TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- try {
- final TextLanguage.Builder builder = new TextLanguage.Builder();
- final LangIdModel.LanguageResult[] langResults =
- getLangIdImpl().detectLanguages(request.getText().toString());
- for (int i = 0; i < langResults.length; i++) {
- builder.putLocale(
- ULocale.forLanguageTag(langResults[i].getLanguage()),
- langResults[i].getScore());
- }
- return builder.build();
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error detecting text language.", t);
- }
- return mFallback.detectLanguage(request);
- }
-
- @Override
- public ConversationActions suggestConversationActions(ConversationActions.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- try {
- ActionsSuggestionsModel actionsImpl = getActionsImpl();
- if (actionsImpl == null) {
- // Actions model is optional, fallback if it is not available.
- return mFallback.suggestConversationActions(request);
- }
- ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- request.getConversation(), this::detectLanguageTagsFromText);
- if (nativeMessages.length == 0) {
- return mFallback.suggestConversationActions(request);
- }
- ActionsSuggestionsModel.Conversation nativeConversation =
- new ActionsSuggestionsModel.Conversation(nativeMessages);
-
- ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
- actionsImpl.suggestActionsWithIntents(
- nativeConversation,
- null,
- mContext,
- getResourceLocalesString(),
- getAnnotatorImpl(LocaleList.getDefault()));
- return createConversationActionResult(request, nativeSuggestions);
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error suggesting conversation actions.", t);
- }
- return mFallback.suggestConversationActions(request);
- }
-
- /**
- * Returns the {@link ConversationAction} result, with a non-null extras.
- * <p>
- * Whenever the RemoteAction is non-null, you can expect its corresponding intent
- * with a non-null component name is in the extras.
- */
- private ConversationActions createConversationActionResult(
- ConversationActions.Request request,
- ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions) {
- Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
- List<ConversationAction> conversationActions = new ArrayList<>();
- for (ActionsSuggestionsModel.ActionSuggestion nativeSuggestion : nativeSuggestions) {
- String actionType = nativeSuggestion.getActionType();
- if (!expectedTypes.contains(actionType)) {
- continue;
- }
- LabeledIntent.Result labeledIntentResult =
- ActionsSuggestionsHelper.createLabeledIntentResult(
- mContext,
- mTemplateIntentFactory,
- nativeSuggestion);
- RemoteAction remoteAction = null;
- Bundle extras = new Bundle();
- if (labeledIntentResult != null) {
- remoteAction = labeledIntentResult.remoteAction;
- ExtrasUtils.putActionIntent(extras, labeledIntentResult.resolvedIntent);
- }
- ExtrasUtils.putSerializedEntityData(extras, nativeSuggestion.getSerializedEntityData());
- ExtrasUtils.putEntitiesExtras(
- extras,
- TemplateIntentFactory.nameVariantsToBundle(nativeSuggestion.getEntityData()));
- conversationActions.add(
- new ConversationAction.Builder(actionType)
- .setConfidenceScore(nativeSuggestion.getScore())
- .setTextReply(nativeSuggestion.getResponseText())
- .setAction(remoteAction)
- .setExtras(extras)
- .build());
- }
- conversationActions =
- ActionsSuggestionsHelper.removeActionsWithDuplicates(conversationActions);
- if (request.getMaxSuggestions() >= 0
- && conversationActions.size() > request.getMaxSuggestions()) {
- conversationActions = conversationActions.subList(0, request.getMaxSuggestions());
- }
- String resultId = ActionsSuggestionsHelper.createResultId(
- mContext,
- request.getConversation(),
- mActionModelInUse.getVersion(),
- mActionModelInUse.getSupportedLocales());
- return new ConversationActions(conversationActions, resultId);
- }
-
- @Nullable
- private String detectLanguageTagsFromText(CharSequence text) {
- if (!mSettings.isDetectLanguagesFromTextEnabled()) {
- return null;
- }
- final float threshold = getLangIdThreshold();
- if (threshold < 0 || threshold > 1) {
- Log.w(LOG_TAG,
- "[detectLanguageTagsFromText] unexpected threshold is found: " + threshold);
- return null;
- }
- TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
- TextLanguage textLanguage = detectLanguage(request);
- int localeHypothesisCount = textLanguage.getLocaleHypothesisCount();
- List<String> languageTags = new ArrayList<>();
- for (int i = 0; i < localeHypothesisCount; i++) {
- ULocale locale = textLanguage.getLocale(i);
- if (textLanguage.getConfidenceScore(locale) < threshold) {
- break;
- }
- languageTags.add(locale.toLanguageTag());
- }
- if (languageTags.isEmpty()) {
- return null;
- }
- return String.join(",", languageTags);
- }
-
- private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
- List<String> defaultActionTypes =
- request.getHints().contains(ConversationActions.Request.HINT_FOR_NOTIFICATION)
- ? mSettings.getNotificationConversationActionTypes()
- : mSettings.getInAppConversationActionTypes();
- return request.getTypeConfig().resolveEntityListModifications(defaultActionTypes);
- }
-
- private AnnotatorModel getAnnotatorImpl(LocaleList localeList)
- throws FileNotFoundException {
- synchronized (mLock) {
- localeList = localeList == null ? LocaleList.getDefault() : localeList;
- final ModelFileManager.ModelFile bestModel =
- mAnnotatorModelFileManager.findBestModelFile(localeList);
- if (bestModel == null) {
- throw new FileNotFoundException(
- "No annotator model for " + localeList.toLanguageTags());
- }
- if (mAnnotatorImpl == null || !Objects.equals(mAnnotatorModelInUse, bestModel)) {
- Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
- new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
- try {
- if (pfd != null) {
- // The current annotator model may be still used by another thread / model.
- // Do not call close() here, and let the GC to clean it up when no one else
- // is using it.
- mAnnotatorImpl = new AnnotatorModel(pfd.getFd());
- mAnnotatorModelInUse = bestModel;
- }
- } finally {
- maybeCloseAndLogError(pfd);
- }
- }
- return mAnnotatorImpl;
- }
- }
-
- private LangIdModel getLangIdImpl() throws FileNotFoundException {
- synchronized (mLock) {
- final ModelFileManager.ModelFile bestModel =
- mLangIdModelFileManager.findBestModelFile(null);
- if (bestModel == null) {
- throw new FileNotFoundException("No LangID model is found");
- }
- if (mLangIdImpl == null || !Objects.equals(mLangIdModelInUse, bestModel)) {
- Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
- new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
- try {
- if (pfd != null) {
- mLangIdImpl = new LangIdModel(pfd.getFd());
- mLangIdModelInUse = bestModel;
- }
- } finally {
- maybeCloseAndLogError(pfd);
- }
- }
- return mLangIdImpl;
- }
- }
-
- @Nullable
- private ActionsSuggestionsModel getActionsImpl() throws FileNotFoundException {
- synchronized (mLock) {
- // TODO: Use LangID to determine the locale we should use here?
- final ModelFileManager.ModelFile bestModel =
- mActionsModelFileManager.findBestModelFile(LocaleList.getDefault());
- if (bestModel == null) {
- return null;
- }
- if (mActionsImpl == null || !Objects.equals(mActionModelInUse, bestModel)) {
- Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
- new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
- try {
- if (pfd == null) {
- Log.d(LOG_TAG, "Failed to read the model file: " + bestModel.getPath());
- return null;
- }
- ActionsModelParams params = mActionsModelParamsSupplier.get();
- mActionsImpl = new ActionsSuggestionsModel(
- pfd.getFd(), params.getSerializedPreconditions(bestModel));
- mActionModelInUse = bestModel;
- } finally {
- maybeCloseAndLogError(pfd);
- }
- }
- return mActionsImpl;
- }
- }
-
- private String createId(String text, int start, int end) {
- synchronized (mLock) {
- return SelectionSessionLogger.createId(text, start, end, mContext,
- mAnnotatorModelInUse.getVersion(),
- mAnnotatorModelInUse.getSupportedLocales());
- }
- }
-
- private static String concatenateLocales(@Nullable LocaleList locales) {
- return (locales == null) ? "" : locales.toLanguageTags();
- }
-
- private TextClassification createClassificationResult(
- AnnotatorModel.ClassificationResult[] classifications,
- String text, int start, int end, @Nullable Instant referenceTime) {
- final String classifiedText = text.substring(start, end);
- final TextClassification.Builder builder = new TextClassification.Builder()
- .setText(classifiedText);
-
- final int typeCount = classifications.length;
- AnnotatorModel.ClassificationResult highestScoringResult =
- typeCount > 0 ? classifications[0] : null;
- for (int i = 0; i < typeCount; i++) {
- builder.setEntityType(classifications[i]);
- if (classifications[i].getScore() > highestScoringResult.getScore()) {
- highestScoringResult = classifications[i];
- }
- }
-
- final Pair<Bundle, Bundle> languagesBundles = generateLanguageBundles(text, start, end);
- final Bundle textLanguagesBundle = languagesBundles.first;
- final Bundle foreignLanguageBundle = languagesBundles.second;
- builder.setForeignLanguageExtra(foreignLanguageBundle);
-
- boolean isPrimaryAction = true;
- final List<LabeledIntent> labeledIntents = mClassificationIntentFactory.create(
- mContext,
- classifiedText,
- foreignLanguageBundle != null,
- referenceTime,
- highestScoringResult);
- final LabeledIntent.TitleChooser titleChooser =
- (labeledIntent, resolveInfo) -> labeledIntent.titleWithoutEntity;
-
- for (LabeledIntent labeledIntent : labeledIntents) {
- final LabeledIntent.Result result =
- labeledIntent.resolve(mContext, titleChooser, textLanguagesBundle);
- if (result == null) {
- continue;
- }
-
- final Intent intent = result.resolvedIntent;
- final RemoteAction action = result.remoteAction;
- if (isPrimaryAction) {
- // For O backwards compatibility, the first RemoteAction is also written to the
- // legacy API fields.
- builder.setIcon(action.getIcon().loadDrawable(mContext));
- builder.setLabel(action.getTitle().toString());
- builder.setIntent(intent);
- builder.setOnClickListener(TextClassification.createIntentOnClickListener(
- TextClassification.createPendingIntent(
- mContext, intent, labeledIntent.requestCode)));
- isPrimaryAction = false;
- }
- builder.addAction(action, intent);
- }
- return builder.setId(createId(text, start, end)).build();
- }
-
- /**
- * Returns a bundle pair with language detection information for extras.
- * <p>
- * Pair.first = textLanguagesBundle - A bundle containing information about all detected
- * languages in the text. May be null if language detection fails or is disabled. This is
- * typically expected to be added to a textClassifier generated remote action intent.
- * See {@link ExtrasUtils#putTextLanguagesExtra(Bundle, Bundle)}.
- * See {@link ExtrasUtils#getTopLanguage(Intent)}.
- * <p>
- * Pair.second = foreignLanguageBundle - A bundle with the language and confidence score if the
- * system finds the text to be in a foreign language. Otherwise is null.
- * See {@link TextClassification.Builder#setForeignLanguageExtra(Bundle)}.
- *
- * @param context the context of the text to detect languages for
- * @param start the start index of the text
- * @param end the end index of the text
- */
- // TODO: Revisit this algorithm.
- // TODO: Consider making this public API.
- private Pair<Bundle, Bundle> generateLanguageBundles(String context, int start, int end) {
- if (!mSettings.isTranslateInClassificationEnabled()) {
- return null;
- }
- try {
- final float threshold = getLangIdThreshold();
- if (threshold < 0 || threshold > 1) {
- Log.w(LOG_TAG,
- "[detectForeignLanguage] unexpected threshold is found: " + threshold);
- return Pair.create(null, null);
- }
-
- final EntityConfidence languageScores = detectLanguages(context, start, end);
- if (languageScores.getEntities().isEmpty()) {
- return Pair.create(null, null);
- }
-
- final Bundle textLanguagesBundle = new Bundle();
- ExtrasUtils.putTopLanguageScores(textLanguagesBundle, languageScores);
-
- final String language = languageScores.getEntities().get(0);
- final float score = languageScores.getConfidenceScore(language);
- if (score < threshold) {
- return Pair.create(textLanguagesBundle, null);
- }
-
- Log.v(LOG_TAG, String.format(
- Locale.US, "Language detected: <%s:%.2f>", language, score));
-
- final Locale detected = new Locale(language);
- final LocaleList deviceLocales = LocaleList.getDefault();
- final int size = deviceLocales.size();
- for (int i = 0; i < size; i++) {
- if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
- return Pair.create(textLanguagesBundle, null);
- }
- }
- final Bundle foreignLanguageBundle = ExtrasUtils.createForeignLanguageExtra(
- detected.getLanguage(), score, getLangIdImpl().getVersion());
- return Pair.create(textLanguagesBundle, foreignLanguageBundle);
- } catch (Throwable t) {
- Log.e(LOG_TAG, "Error generating language bundles.", t);
- }
- return Pair.create(null, null);
- }
-
- /**
- * Detect the language of a piece of text by taking surrounding text into consideration.
- *
- * @param text text providing context for the text for which its language is to be detected
- * @param start the start index of the text to detect its language
- * @param end the end index of the text to detect its language
- */
- // TODO: Revisit this algorithm.
- private EntityConfidence detectLanguages(String text, int start, int end)
- throws FileNotFoundException {
- Preconditions.checkArgument(start >= 0);
- Preconditions.checkArgument(end <= text.length());
- Preconditions.checkArgument(start <= end);
-
- final float[] langIdContextSettings = mSettings.getLangIdContextSettings();
- // The minimum size of text to prefer for detection.
- final int minimumTextSize = (int) langIdContextSettings[0];
- // For reducing the score when text is less than the preferred size.
- final float penalizeRatio = langIdContextSettings[1];
- // Original detection score to surrounding text detection score ratios.
- final float subjectTextScoreRatio = langIdContextSettings[2];
- final float moreTextScoreRatio = 1f - subjectTextScoreRatio;
- Log.v(LOG_TAG,
- String.format(Locale.US, "LangIdContextSettings: "
- + "minimumTextSize=%d, penalizeRatio=%.2f, "
- + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
- minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio));
-
- if (end - start < minimumTextSize && penalizeRatio <= 0) {
- return new EntityConfidence(Collections.emptyMap());
- }
-
- final String subject = text.substring(start, end);
- final EntityConfidence scores = detectLanguages(subject);
-
- if (subject.length() >= minimumTextSize
- || subject.length() == text.length()
- || subjectTextScoreRatio * penalizeRatio >= 1) {
- return scores;
- }
-
- final EntityConfidence moreTextScores;
- if (moreTextScoreRatio >= 0) {
- // Attempt to grow the detection text to be at least minimumTextSize long.
- final String moreText = Utils.getSubString(text, start, end, minimumTextSize);
- moreTextScores = detectLanguages(moreText);
- } else {
- moreTextScores = new EntityConfidence(Collections.emptyMap());
- }
-
- // Combine the original detection scores with the those returned after including more text.
- final Map<String, Float> newScores = new ArrayMap<>();
- final Set<String> languages = new ArraySet<>();
- languages.addAll(scores.getEntities());
- languages.addAll(moreTextScores.getEntities());
- for (String language : languages) {
- final float score = (subjectTextScoreRatio * scores.getConfidenceScore(language)
- + moreTextScoreRatio * moreTextScores.getConfidenceScore(language))
- * penalizeRatio;
- newScores.put(language, score);
- }
- return new EntityConfidence(newScores);
- }
-
- /**
- * Detect languages for the specified text.
- */
- private EntityConfidence detectLanguages(String text) throws FileNotFoundException {
- final LangIdModel langId = getLangIdImpl();
- final LangIdModel.LanguageResult[] langResults = langId.detectLanguages(text);
- final Map<String, Float> languagesMap = new ArrayMap<>();
- for (LanguageResult langResult : langResults) {
- languagesMap.put(langResult.getLanguage(), langResult.getScore());
- }
- return new EntityConfidence(languagesMap);
- }
-
- private float getLangIdThreshold() {
- try {
- return mSettings.getLangIdThresholdOverride() >= 0
- ? mSettings.getLangIdThresholdOverride()
- : getLangIdImpl().getLangIdThreshold();
- } catch (FileNotFoundException e) {
- final float defaultThreshold = 0.5f;
- Log.v(LOG_TAG, "Using default foreign language threshold: " + defaultThreshold);
- return defaultThreshold;
- }
- }
-
- @Override
- public void dump(@NonNull IndentingPrintWriter printWriter) {
- synchronized (mLock) {
- printWriter.println("TextClassifierImpl:");
- printWriter.increaseIndent();
- printWriter.println("Annotator model file(s):");
- printWriter.increaseIndent();
- for (ModelFileManager.ModelFile modelFile :
- mAnnotatorModelFileManager.listModelFiles()) {
- printWriter.println(modelFile.toString());
- }
- printWriter.decreaseIndent();
- printWriter.println("LangID model file(s):");
- printWriter.increaseIndent();
- for (ModelFileManager.ModelFile modelFile :
- mLangIdModelFileManager.listModelFiles()) {
- printWriter.println(modelFile.toString());
- }
- printWriter.decreaseIndent();
- printWriter.println("Actions model file(s):");
- printWriter.increaseIndent();
- for (ModelFileManager.ModelFile modelFile :
- mActionsModelFileManager.listModelFiles()) {
- printWriter.println(modelFile.toString());
- }
- printWriter.decreaseIndent();
- printWriter.printPair("mFallback", mFallback);
- printWriter.decreaseIndent();
- printWriter.println();
- }
- }
-
- /**
- * Closes the ParcelFileDescriptor, if non-null, and logs any errors that occur.
- */
- private static void maybeCloseAndLogError(@Nullable ParcelFileDescriptor fd) {
- if (fd == null) {
- return;
- }
-
- try {
- fd.close();
- } catch (IOException e) {
- Log.e(LOG_TAG, "Error closing file.", e);
- }
- }
-
- /**
- * Returns the locales string for the current resources configuration.
- */
- private String getResourceLocalesString() {
- try {
- return mContext.getResources().getConfiguration().getLocales().toLanguageTags();
- } catch (NullPointerException e) {
- // NPE is unexpected. Erring on the side of caution.
- return LocaleList.getDefault().toLanguageTags();
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index 58024dc..1e8253d 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -20,12 +20,10 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.icu.util.ULocale;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
@@ -227,10 +225,7 @@
private final CharSequence mText;
private final Bundle mExtra;
- @Nullable private String mCallingPackageName;
- @UserIdInt
- private int mUserId = UserHandle.USER_NULL;
- private boolean mUseDefaultTextClassifier;
+ @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
private Request(CharSequence text, Bundle bundle) {
mText = text;
@@ -246,61 +241,33 @@
}
/**
- * Sets the name of the package that is sending this request.
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void setCallingPackageName(@Nullable String callingPackageName) {
- mCallingPackageName = callingPackageName;
- }
-
- /**
* Returns the name of the package that sent this request.
* This returns null if no calling package name is set.
*/
@Nullable
public String getCallingPackageName() {
- return mCallingPackageName;
+ return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
}
/**
- * Sets the id of the user that sent this request.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- void setUserId(@UserIdInt int userId) {
- mUserId = userId;
- }
-
- /**
- * Returns the id of the user that sent this request.
- * @hide
- */
- @UserIdInt
- public int getUserId() {
- return mUserId;
- }
-
- /**
- * Sets whether to use the default text classifier to handle this request.
- * This will be ignored if it is not the system text classifier to handle this request.
+ * Sets the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
- mUseDefaultTextClassifier = useDefaultTextClassifier;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setSystemTextClassifierMetadata(
+ @Nullable SystemTextClassifierMetadata systemTcMetadata) {
+ mSystemTcMetadata = systemTcMetadata;
}
/**
- * Returns whether to use the default text classifier to handle this request. This
- * will be ignored if it is not the system text classifier to handle this request.
+ * Returns the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- public boolean getUseDefaultTextClassifier() {
- return mUseDefaultTextClassifier;
+ @Nullable
+ public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+ return mSystemTcMetadata;
}
/**
@@ -321,23 +288,17 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequence(mText);
- dest.writeString(mCallingPackageName);
- dest.writeInt(mUserId);
dest.writeBundle(mExtra);
- dest.writeBoolean(mUseDefaultTextClassifier);
+ dest.writeParcelable(mSystemTcMetadata, flags);
}
private static Request readFromParcel(Parcel in) {
final CharSequence text = in.readCharSequence();
- final String callingPackageName = in.readString();
- final int userId = in.readInt();
final Bundle extra = in.readBundle();
- final boolean useDefaultTextClassifier = in.readBoolean();
+ final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
final Request request = new Request(text, extra);
- request.setCallingPackageName(callingPackageName);
- request.setUserId(userId);
- request.setUseDefaultTextClassifier(useDefaultTextClassifier);
+ request.setSystemTextClassifierMetadata(systemTcMetadata);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 7430cb3..dea3a90 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -20,13 +20,11 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.Context;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.text.Spannable;
import android.text.method.MovementMethod;
import android.text.style.ClickableSpan;
@@ -340,12 +338,9 @@
@Nullable private final LocaleList mDefaultLocales;
@Nullable private final EntityConfig mEntityConfig;
private final boolean mLegacyFallback;
- @Nullable private String mCallingPackageName;
private final Bundle mExtras;
@Nullable private final ZonedDateTime mReferenceTime;
- @UserIdInt
- private int mUserId = UserHandle.USER_NULL;
- private boolean mUseDefaultTextClassifier;
+ @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
private Request(
CharSequence text,
@@ -409,62 +404,33 @@
}
/**
- * Sets the name of the package that is sending this request.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void setCallingPackageName(@Nullable String callingPackageName) {
- mCallingPackageName = callingPackageName;
- }
-
- /**
* Returns the name of the package that sent this request.
* This returns {@code null} if no calling package name is set.
*/
@Nullable
public String getCallingPackageName() {
- return mCallingPackageName;
+ return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
}
/**
- * Sets the id of the user that sent this request.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- void setUserId(@UserIdInt int userId) {
- mUserId = userId;
- }
-
- /**
- * Returns the id of the user that sent this request.
- * @hide
- */
- @UserIdInt
- public int getUserId() {
- return mUserId;
- }
-
- /**
- * Sets whether to use the default text classifier to handle this request.
- * This will be ignored if it is not the system text classifier to handle this request.
+ * Sets the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
- mUseDefaultTextClassifier = useDefaultTextClassifier;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setSystemTextClassifierMetadata(
+ @Nullable SystemTextClassifierMetadata systemTcMetadata) {
+ mSystemTcMetadata = systemTcMetadata;
}
/**
- * Returns whether to use the default text classifier to handle this request. This
- * will be ignored if it is not the system text classifier to handle this request.
+ * Returns the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- public boolean getUseDefaultTextClassifier() {
- return mUseDefaultTextClassifier;
+ @Nullable
+ public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+ return mSystemTcMetadata;
}
/**
@@ -585,30 +551,24 @@
dest.writeString(mText.toString());
dest.writeParcelable(mDefaultLocales, flags);
dest.writeParcelable(mEntityConfig, flags);
- dest.writeString(mCallingPackageName);
- dest.writeInt(mUserId);
dest.writeBundle(mExtras);
dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
- dest.writeBoolean(mUseDefaultTextClassifier);
+ dest.writeParcelable(mSystemTcMetadata, flags);
}
private static Request readFromParcel(Parcel in) {
final String text = in.readString();
final LocaleList defaultLocales = in.readParcelable(null);
final EntityConfig entityConfig = in.readParcelable(null);
- final String callingPackageName = in.readString();
- final int userId = in.readInt();
final Bundle extras = in.readBundle();
final String referenceTimeString = in.readString();
final ZonedDateTime referenceTime = referenceTimeString == null
? null : ZonedDateTime.parse(referenceTimeString);
- final boolean useDefaultTextClassifier = in.readBoolean();
+ final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
final Request request = new Request(text, defaultLocales, entityConfig,
/* legacyFallback= */ true, referenceTime, extras);
- request.setCallingPackageName(callingPackageName);
- request.setUserId(userId);
- request.setUseDefaultTextClassifier(useDefaultTextClassifier);
+ request.setSystemTextClassifierMetadata(systemTcMetadata);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 575a072..d8a632d 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -20,12 +20,10 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.textclassifier.TextClassifier.EntityType;
@@ -213,10 +211,7 @@
@Nullable private final LocaleList mDefaultLocales;
private final boolean mDarkLaunchAllowed;
private final Bundle mExtras;
- @Nullable private String mCallingPackageName;
- @UserIdInt
- private int mUserId = UserHandle.USER_NULL;
- private boolean mUseDefaultTextClassifier;
+ @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
private Request(
CharSequence text,
@@ -278,62 +273,33 @@
}
/**
- * Sets the name of the package that is sending this request.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void setCallingPackageName(@Nullable String callingPackageName) {
- mCallingPackageName = callingPackageName;
- }
-
- /**
* Returns the name of the package that sent this request.
* This returns {@code null} if no calling package name is set.
*/
@Nullable
public String getCallingPackageName() {
- return mCallingPackageName;
+ return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
}
/**
- * Sets the id of the user that sent this request.
- * <p>
- * Package-private for SystemTextClassifier's use.
- * @hide
- */
- void setUserId(@UserIdInt int userId) {
- mUserId = userId;
- }
-
- /**
- * Returns the id of the user that sent this request.
- * @hide
- */
- @UserIdInt
- public int getUserId() {
- return mUserId;
- }
-
- /**
- * Sets whether to use the default text classifier to handle this request.
- * This will be ignored if it is not the system text classifier to handle this request.
+ * Sets the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
- mUseDefaultTextClassifier = useDefaultTextClassifier;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setSystemTextClassifierMetadata(
+ @Nullable SystemTextClassifierMetadata systemTcMetadata) {
+ mSystemTcMetadata = systemTcMetadata;
}
/**
- * Returns whether to use the default text classifier to handle this request. This
- * will be ignored if it is not the system text classifier to handle this request.
+ * Returns the information about the {@link SystemTextClassifier} that sent this request.
*
* @hide
*/
- public boolean getUseDefaultTextClassifier() {
- return mUseDefaultTextClassifier;
+ @Nullable
+ public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
+ return mSystemTcMetadata;
}
/**
@@ -438,10 +404,8 @@
dest.writeInt(mStartIndex);
dest.writeInt(mEndIndex);
dest.writeParcelable(mDefaultLocales, flags);
- dest.writeString(mCallingPackageName);
- dest.writeInt(mUserId);
dest.writeBundle(mExtras);
- dest.writeBoolean(mUseDefaultTextClassifier);
+ dest.writeParcelable(mSystemTcMetadata, flags);
}
private static Request readFromParcel(Parcel in) {
@@ -449,16 +413,12 @@
final int startIndex = in.readInt();
final int endIndex = in.readInt();
final LocaleList defaultLocales = in.readParcelable(null);
- final String callingPackageName = in.readString();
- final int userId = in.readInt();
final Bundle extras = in.readBundle();
- final boolean systemTextClassifierType = in.readBoolean();
+ final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
final Request request = new Request(text, startIndex, endIndex, defaultLocales,
/* darkLaunchAllowed= */ false, extras);
- request.setCallingPackageName(callingPackageName);
- request.setUserId(userId);
- request.setUseDefaultTextClassifier(systemTextClassifierType);
+ request.setSystemTextClassifierMetadata(systemTcMetadata);
return request;
}
diff --git a/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
deleted file mode 100644
index 22e374f2..0000000
--- a/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import java.time.Instant;
-import java.util.List;
-
-/**
- * @hide
- */
-public interface ClassificationIntentFactory {
-
- /**
- * Return a list of LabeledIntent from the classification result.
- */
- List<LabeledIntent> create(
- Context context,
- String text,
- boolean foreignText,
- @Nullable Instant referenceTime,
- @Nullable AnnotatorModel.ClassificationResult classification);
-
- /**
- * Inserts translate action to the list if it is a foreign text.
- */
- static void insertTranslateAction(
- List<LabeledIntent> actions, Context context, String text) {
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.translate),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.translate_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_TRANSLATE)
- // TODO: Probably better to introduce a "translate" scheme instead of
- // using EXTRA_TEXT.
- .putExtra(Intent.EXTRA_TEXT, text),
- text.hashCode()));
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/LabeledIntent.java b/core/java/android/view/textclassifier/intent/LabeledIntent.java
deleted file mode 100644
index cbd9d1a..0000000
--- a/core/java/android/view/textclassifier/intent/LabeledIntent.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.textclassifier.ExtrasUtils;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Objects;
-
-/**
- * Helper class to store the information from which RemoteActions are built.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class LabeledIntent {
- private static final String TAG = "LabeledIntent";
- public static final int DEFAULT_REQUEST_CODE = 0;
- private static final TitleChooser DEFAULT_TITLE_CHOOSER =
- (labeledIntent, resolveInfo) -> {
- if (!TextUtils.isEmpty(labeledIntent.titleWithEntity)) {
- return labeledIntent.titleWithEntity;
- }
- return labeledIntent.titleWithoutEntity;
- };
-
- @Nullable
- public final String titleWithoutEntity;
- @Nullable
- public final String titleWithEntity;
- public final String description;
- @Nullable
- public final String descriptionWithAppName;
- // Do not update this intent.
- public final Intent intent;
- public final int requestCode;
-
- /**
- * Initializes a LabeledIntent.
- *
- * <p>NOTE: {@code requestCode} is required to not be {@link #DEFAULT_REQUEST_CODE}
- * if distinguishing info (e.g. the classified text) is represented in intent extras only.
- * In such circumstances, the request code should represent the distinguishing info
- * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat)
- * unique. To be correct, the PendingIntent should be definitely unique but we try a
- * best effort approach that avoids spamming the system with PendingIntents.
- */
- // TODO: Fix the issue mentioned above so the behaviour is correct.
- public LabeledIntent(
- @Nullable String titleWithoutEntity,
- @Nullable String titleWithEntity,
- String description,
- @Nullable String descriptionWithAppName,
- Intent intent,
- int requestCode) {
- if (TextUtils.isEmpty(titleWithEntity) && TextUtils.isEmpty(titleWithoutEntity)) {
- throw new IllegalArgumentException(
- "titleWithEntity and titleWithoutEntity should not be both null");
- }
- this.titleWithoutEntity = titleWithoutEntity;
- this.titleWithEntity = titleWithEntity;
- this.description = Objects.requireNonNull(description);
- this.descriptionWithAppName = descriptionWithAppName;
- this.intent = Objects.requireNonNull(intent);
- this.requestCode = requestCode;
- }
-
- /**
- * Return the resolved result.
- *
- * @param context the context to resolve the result's intent and action
- * @param titleChooser for choosing an action title
- * @param textLanguagesBundle containing language detection information
- */
- @Nullable
- public Result resolve(
- Context context,
- @Nullable TitleChooser titleChooser,
- @Nullable Bundle textLanguagesBundle) {
- final PackageManager pm = context.getPackageManager();
- final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
-
- if (resolveInfo == null || resolveInfo.activityInfo == null) {
- Log.w(TAG, "resolveInfo or activityInfo is null");
- return null;
- }
- final String packageName = resolveInfo.activityInfo.packageName;
- final String className = resolveInfo.activityInfo.name;
- if (packageName == null || className == null) {
- Log.w(TAG, "packageName or className is null");
- return null;
- }
- Intent resolvedIntent = new Intent(intent);
- resolvedIntent.putExtra(
- TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER,
- getFromTextClassifierExtra(textLanguagesBundle));
- boolean shouldShowIcon = false;
- Icon icon = null;
- if (!"android".equals(packageName)) {
- // We only set the component name when the package name is not resolved to "android"
- // to workaround a bug that explicit intent with component name == ResolverActivity
- // can't be launched on keyguard.
- resolvedIntent.setComponent(new ComponentName(packageName, className));
- if (resolveInfo.activityInfo.getIconResource() != 0) {
- icon = Icon.createWithResource(
- packageName, resolveInfo.activityInfo.getIconResource());
- shouldShowIcon = true;
- }
- }
- if (icon == null) {
- // RemoteAction requires that there be an icon.
- icon = Icon.createWithResource(
- "android", com.android.internal.R.drawable.ic_more_items);
- }
- final PendingIntent pendingIntent =
- TextClassification.createPendingIntent(context, resolvedIntent, requestCode);
- titleChooser = titleChooser == null ? DEFAULT_TITLE_CHOOSER : titleChooser;
- CharSequence title = titleChooser.chooseTitle(this, resolveInfo);
- if (TextUtils.isEmpty(title)) {
- Log.w(TAG, "Custom titleChooser return null, fallback to the default titleChooser");
- title = DEFAULT_TITLE_CHOOSER.chooseTitle(this, resolveInfo);
- }
- final RemoteAction action =
- new RemoteAction(icon, title, resolveDescription(resolveInfo, pm), pendingIntent);
- action.setShouldShowIcon(shouldShowIcon);
- return new Result(resolvedIntent, action);
- }
-
- private String resolveDescription(ResolveInfo resolveInfo, PackageManager packageManager) {
- if (!TextUtils.isEmpty(descriptionWithAppName)) {
- // Example string format of descriptionWithAppName: "Use %1$s to open map".
- String applicationName = getApplicationName(resolveInfo, packageManager);
- if (!TextUtils.isEmpty(applicationName)) {
- return String.format(descriptionWithAppName, applicationName);
- }
- }
- return description;
- }
-
- @Nullable
- private String getApplicationName(
- ResolveInfo resolveInfo, PackageManager packageManager) {
- if (resolveInfo.activityInfo == null) {
- return null;
- }
- if ("android".equals(resolveInfo.activityInfo.packageName)) {
- return null;
- }
- if (resolveInfo.activityInfo.applicationInfo == null) {
- return null;
- }
- return (String) packageManager.getApplicationLabel(
- resolveInfo.activityInfo.applicationInfo);
- }
-
- private Bundle getFromTextClassifierExtra(@Nullable Bundle textLanguagesBundle) {
- if (textLanguagesBundle != null) {
- final Bundle bundle = new Bundle();
- ExtrasUtils.putTextLanguagesExtra(bundle, textLanguagesBundle);
- return bundle;
- } else {
- return Bundle.EMPTY;
- }
- }
-
- /**
- * Data class that holds the result.
- */
- public static final class Result {
- public final Intent resolvedIntent;
- public final RemoteAction remoteAction;
-
- public Result(Intent resolvedIntent, RemoteAction remoteAction) {
- this.resolvedIntent = Objects.requireNonNull(resolvedIntent);
- this.remoteAction = Objects.requireNonNull(remoteAction);
- }
- }
-
- /**
- * An object to choose a title from resolved info. If {@code null} is returned,
- * {@link #titleWithEntity} will be used if it exists, {@link #titleWithoutEntity} otherwise.
- */
- public interface TitleChooser {
- /**
- * Picks a title from a {@link LabeledIntent} by looking into resolved info.
- * {@code resolveInfo} is guaranteed to have a non-null {@code activityInfo}.
- */
- @Nullable
- CharSequence chooseTitle(LabeledIntent labeledIntent, ResolveInfo resolveInfo);
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
deleted file mode 100644
index 8d60ad8..0000000
--- a/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import static java.time.temporal.ChronoUnit.MILLIS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.SearchManager;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.provider.Browser;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Creates intents based on the classification type.
- * @hide
- */
-// TODO: Consider to support {@code descriptionWithAppName}.
-public final class LegacyClassificationIntentFactory implements ClassificationIntentFactory {
-
- private static final String TAG = "LegacyClassificationIntentFactory";
- private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
- private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
-
- @NonNull
- @Override
- public List<LabeledIntent> create(Context context, String text, boolean foreignText,
- @Nullable Instant referenceTime,
- AnnotatorModel.ClassificationResult classification) {
- final String type = classification != null
- ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
- : "";
- text = text.trim();
- final List<LabeledIntent> actions;
- switch (type) {
- case TextClassifier.TYPE_EMAIL:
- actions = createForEmail(context, text);
- break;
- case TextClassifier.TYPE_PHONE:
- actions = createForPhone(context, text);
- break;
- case TextClassifier.TYPE_ADDRESS:
- actions = createForAddress(context, text);
- break;
- case TextClassifier.TYPE_URL:
- actions = createForUrl(context, text);
- break;
- case TextClassifier.TYPE_DATE: // fall through
- case TextClassifier.TYPE_DATE_TIME:
- if (classification.getDatetimeResult() != null) {
- final Instant parsedTime = Instant.ofEpochMilli(
- classification.getDatetimeResult().getTimeMsUtc());
- actions = createForDatetime(context, type, referenceTime, parsedTime);
- } else {
- actions = new ArrayList<>();
- }
- break;
- case TextClassifier.TYPE_FLIGHT_NUMBER:
- actions = createForFlight(context, text);
- break;
- case TextClassifier.TYPE_DICTIONARY:
- actions = createForDictionary(context, text);
- break;
- default:
- actions = new ArrayList<>();
- break;
- }
- if (foreignText) {
- ClassificationIntentFactory.insertTranslateAction(actions, context, text);
- }
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForEmail(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.email),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.email_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("mailto:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.add_contact),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.add_contact_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
- text.hashCode()));
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForPhone(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- final UserManager userManager = context.getSystemService(UserManager.class);
- final Bundle userRestrictions = userManager != null
- ? userManager.getUserRestrictions() : new Bundle();
- if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) {
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.dial),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.dial_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_DIAL).setData(
- Uri.parse(String.format("tel:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- }
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.add_contact),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.add_contact_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.PHONE, text),
- text.hashCode()));
- if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.sms),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.sms_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("smsto:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- }
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForAddress(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- try {
- final String encText = URLEncoder.encode(text, "UTF-8");
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.map),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.map_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_VIEW)
- .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- } catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Could not encode address", e);
- }
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForUrl(Context context, String text) {
- if (Uri.parse(text).getScheme() == null) {
- text = "http://" + text;
- }
- final List<LabeledIntent> actions = new ArrayList<>();
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.browse),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.browse_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_VIEW)
- .setDataAndNormalize(Uri.parse(text))
- .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForDatetime(
- Context context, String type, @Nullable Instant referenceTime,
- Instant parsedTime) {
- if (referenceTime == null) {
- // If no reference time was given, use now.
- referenceTime = Instant.now();
- }
- List<LabeledIntent> actions = new ArrayList<>();
- actions.add(createCalendarViewIntent(context, parsedTime));
- final long millisUntilEvent = referenceTime.until(parsedTime, MILLIS);
- if (millisUntilEvent > MIN_EVENT_FUTURE_MILLIS) {
- actions.add(createCalendarCreateEventIntent(context, parsedTime, type));
- }
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForFlight(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.view_flight),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.view_flight_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_WEB_SEARCH)
- .putExtra(SearchManager.QUERY, text),
- text.hashCode()));
- return actions;
- }
-
- @NonNull
- private static LabeledIntent createCalendarViewIntent(Context context, Instant parsedTime) {
- Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
- builder.appendPath("time");
- ContentUris.appendId(builder, parsedTime.toEpochMilli());
- return new LabeledIntent(
- context.getString(com.android.internal.R.string.view_calendar),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.view_calendar_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_VIEW).setData(builder.build()),
- LabeledIntent.DEFAULT_REQUEST_CODE);
- }
-
- @NonNull
- private static LabeledIntent createCalendarCreateEventIntent(
- Context context, Instant parsedTime, @TextClassifier.EntityType String type) {
- final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
- return new LabeledIntent(
- context.getString(com.android.internal.R.string.add_calendar_event),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.add_calendar_event_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_INSERT)
- .setData(CalendarContract.Events.CONTENT_URI)
- .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
- .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
- parsedTime.toEpochMilli())
- .putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
- parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
- parsedTime.hashCode());
- }
-
- @NonNull
- private static List<LabeledIntent> createForDictionary(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.define),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.define_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_DEFINE)
- .putExtra(Intent.EXTRA_TEXT, text),
- text.hashCode()));
- return actions;
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java
deleted file mode 100644
index aef4bd6..0000000
--- a/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.time.Instant;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Creates intents based on {@link RemoteActionTemplate} objects for a ClassificationResult.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class TemplateClassificationIntentFactory implements ClassificationIntentFactory {
- private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
- private final TemplateIntentFactory mTemplateIntentFactory;
- private final ClassificationIntentFactory mFallback;
-
- public TemplateClassificationIntentFactory(TemplateIntentFactory templateIntentFactory,
- ClassificationIntentFactory fallback) {
- mTemplateIntentFactory = Objects.requireNonNull(templateIntentFactory);
- mFallback = Objects.requireNonNull(fallback);
- }
-
- /**
- * Returns a list of {@link LabeledIntent}
- * that are constructed from the classification result.
- */
- @NonNull
- @Override
- public List<LabeledIntent> create(
- Context context,
- String text,
- boolean foreignText,
- @Nullable Instant referenceTime,
- @Nullable AnnotatorModel.ClassificationResult classification) {
- if (classification == null) {
- return Collections.emptyList();
- }
- RemoteActionTemplate[] remoteActionTemplates = classification.getRemoteActionTemplates();
- if (remoteActionTemplates == null) {
- // RemoteActionTemplate is missing, fallback.
- Log.w(TAG, "RemoteActionTemplate is missing, fallback to"
- + " LegacyClassificationIntentFactory.");
- return mFallback.create(context, text, foreignText, referenceTime, classification);
- }
- final List<LabeledIntent> labeledIntents =
- mTemplateIntentFactory.create(remoteActionTemplates);
- if (foreignText) {
- ClassificationIntentFactory.insertTranslateAction(labeledIntents, context, text.trim());
- }
- return labeledIntents;
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java b/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
deleted file mode 100644
index 7a39569..0000000
--- a/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.android.textclassifier.NamedVariant;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Creates intents based on {@link RemoteActionTemplate} objects.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class TemplateIntentFactory {
- private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
-
- /**
- * Constructs and returns a list of {@link LabeledIntent} based on the given templates.
- */
- @Nullable
- public List<LabeledIntent> create(
- @NonNull RemoteActionTemplate[] remoteActionTemplates) {
- if (remoteActionTemplates.length == 0) {
- return new ArrayList<>();
- }
- final List<LabeledIntent> labeledIntents = new ArrayList<>();
- for (RemoteActionTemplate remoteActionTemplate : remoteActionTemplates) {
- if (!isValidTemplate(remoteActionTemplate)) {
- Log.w(TAG, "Invalid RemoteActionTemplate skipped.");
- continue;
- }
- labeledIntents.add(
- new LabeledIntent(
- remoteActionTemplate.titleWithoutEntity,
- remoteActionTemplate.titleWithEntity,
- remoteActionTemplate.description,
- remoteActionTemplate.descriptionWithAppName,
- createIntent(remoteActionTemplate),
- remoteActionTemplate.requestCode == null
- ? LabeledIntent.DEFAULT_REQUEST_CODE
- : remoteActionTemplate.requestCode));
- }
- return labeledIntents;
- }
-
- private static boolean isValidTemplate(@Nullable RemoteActionTemplate remoteActionTemplate) {
- if (remoteActionTemplate == null) {
- Log.w(TAG, "Invalid RemoteActionTemplate: is null");
- return false;
- }
- if (TextUtils.isEmpty(remoteActionTemplate.titleWithEntity)
- && TextUtils.isEmpty(remoteActionTemplate.titleWithoutEntity)) {
- Log.w(TAG, "Invalid RemoteActionTemplate: title is null");
- return false;
- }
- if (TextUtils.isEmpty(remoteActionTemplate.description)) {
- Log.w(TAG, "Invalid RemoteActionTemplate: description is null");
- return false;
- }
- if (!TextUtils.isEmpty(remoteActionTemplate.packageName)) {
- Log.w(TAG, "Invalid RemoteActionTemplate: package name is set");
- return false;
- }
- if (TextUtils.isEmpty(remoteActionTemplate.action)) {
- Log.w(TAG, "Invalid RemoteActionTemplate: intent action not set");
- return false;
- }
- return true;
- }
-
- private static Intent createIntent(RemoteActionTemplate remoteActionTemplate) {
- final Intent intent = new Intent(remoteActionTemplate.action);
- final Uri uri = TextUtils.isEmpty(remoteActionTemplate.data)
- ? null : Uri.parse(remoteActionTemplate.data).normalizeScheme();
- final String type = TextUtils.isEmpty(remoteActionTemplate.type)
- ? null : Intent.normalizeMimeType(remoteActionTemplate.type);
- intent.setDataAndType(uri, type);
- intent.setFlags(remoteActionTemplate.flags == null ? 0 : remoteActionTemplate.flags);
- if (remoteActionTemplate.category != null) {
- for (String category : remoteActionTemplate.category) {
- if (category != null) {
- intent.addCategory(category);
- }
- }
- }
- intent.putExtras(nameVariantsToBundle(remoteActionTemplate.extras));
- return intent;
- }
-
- /**
- * Converts an array of {@link NamedVariant} to a Bundle and returns it.
- */
- public static Bundle nameVariantsToBundle(@Nullable NamedVariant[] namedVariants) {
- if (namedVariants == null) {
- return Bundle.EMPTY;
- }
- Bundle bundle = new Bundle();
- for (NamedVariant namedVariant : namedVariants) {
- if (namedVariant == null) {
- continue;
- }
- switch (namedVariant.getType()) {
- case NamedVariant.TYPE_INT:
- bundle.putInt(namedVariant.getName(), namedVariant.getInt());
- break;
- case NamedVariant.TYPE_LONG:
- bundle.putLong(namedVariant.getName(), namedVariant.getLong());
- break;
- case NamedVariant.TYPE_FLOAT:
- bundle.putFloat(namedVariant.getName(), namedVariant.getFloat());
- break;
- case NamedVariant.TYPE_DOUBLE:
- bundle.putDouble(namedVariant.getName(), namedVariant.getDouble());
- break;
- case NamedVariant.TYPE_BOOL:
- bundle.putBoolean(namedVariant.getName(), namedVariant.getBool());
- break;
- case NamedVariant.TYPE_STRING:
- bundle.putString(namedVariant.getName(), namedVariant.getString());
- break;
- default:
- Log.w(TAG,
- "Unsupported type found in nameVariantsToBundle : "
- + namedVariant.getType());
- }
- }
- return bundle;
- }
-}
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
deleted file mode 100644
index 28cb80d..0000000
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.os.Build;
-import android.util.Log;
-import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextSelection;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * A selection event tracker.
- * @hide
- */
-//TODO: Do not allow any crashes from this class.
-public final class SmartSelectionEventTracker {
-
- private static final String LOG_TAG = "SmartSelectEventTracker";
- private static final boolean DEBUG_LOG_ENABLED = true;
-
- private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
- private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
- private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
- private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
- private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
- private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
- private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
- private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
- private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
- private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
- private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
- private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
-
- private static final String ZERO = "0";
- private static final String TEXTVIEW = "textview";
- private static final String EDITTEXT = "edittext";
- private static final String UNSELECTABLE_TEXTVIEW = "nosel-textview";
- private static final String WEBVIEW = "webview";
- private static final String EDIT_WEBVIEW = "edit-webview";
- private static final String CUSTOM_TEXTVIEW = "customview";
- private static final String CUSTOM_EDITTEXT = "customedit";
- private static final String CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
- private static final String UNKNOWN = "unknown";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({WidgetType.UNSPECIFIED, WidgetType.TEXTVIEW, WidgetType.WEBVIEW,
- WidgetType.EDITTEXT, WidgetType.EDIT_WEBVIEW})
- public @interface WidgetType {
- int UNSPECIFIED = 0;
- int TEXTVIEW = 1;
- int WEBVIEW = 2;
- int EDITTEXT = 3;
- int EDIT_WEBVIEW = 4;
- int UNSELECTABLE_TEXTVIEW = 5;
- int CUSTOM_TEXTVIEW = 6;
- int CUSTOM_EDITTEXT = 7;
- int CUSTOM_UNSELECTABLE_TEXTVIEW = 8;
- }
-
- private final MetricsLogger mMetricsLogger = new MetricsLogger();
- private final int mWidgetType;
- @Nullable private final String mWidgetVersion;
- private final Context mContext;
-
- @Nullable private String mSessionId;
- private final int[] mSmartIndices = new int[2];
- private final int[] mPrevIndices = new int[2];
- private int mOrigStart;
- private int mIndex;
- private long mSessionStartTime;
- private long mLastEventTime;
- private boolean mSmartSelectionTriggered;
- private String mModelName;
-
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
- mWidgetType = widgetType;
- mWidgetVersion = null;
- mContext = Objects.requireNonNull(context);
- }
-
- public SmartSelectionEventTracker(
- @NonNull Context context, @WidgetType int widgetType, @Nullable String widgetVersion) {
- mWidgetType = widgetType;
- mWidgetVersion = widgetVersion;
- mContext = Objects.requireNonNull(context);
- }
-
- /**
- * Logs a selection event.
- *
- * @param event the selection event
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public void logEvent(@NonNull SelectionEvent event) {
- Objects.requireNonNull(event);
-
- if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null
- && DEBUG_LOG_ENABLED) {
- Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
- return;
- }
-
- final long now = System.currentTimeMillis();
- switch (event.mEventType) {
- case SelectionEvent.EventType.SELECTION_STARTED:
- mSessionId = startNewSession();
- Preconditions.checkArgument(event.mEnd == event.mStart + 1);
- mOrigStart = event.mStart;
- mSessionStartTime = now;
- break;
- case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through
- case SelectionEvent.EventType.SMART_SELECTION_MULTI:
- mSmartSelectionTriggered = true;
- mModelName = getModelName(event);
- mSmartIndices[0] = event.mStart;
- mSmartIndices[1] = event.mEnd;
- break;
- case SelectionEvent.EventType.SELECTION_MODIFIED: // fall through
- case SelectionEvent.EventType.AUTO_SELECTION:
- if (mPrevIndices[0] == event.mStart && mPrevIndices[1] == event.mEnd) {
- // Selection did not change. Ignore event.
- return;
- }
- }
- writeEvent(event, now);
-
- if (event.isTerminal()) {
- endSession();
- }
- }
-
- private void writeEvent(SelectionEvent event, long now) {
- final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime;
- final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
- .setType(getLogType(event))
- .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
- .setPackageName(mContext.getPackageName())
- .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime)
- .addTaggedData(PREV_EVENT_DELTA, prevEventDelta)
- .addTaggedData(INDEX, mIndex)
- .addTaggedData(WIDGET_TYPE, getWidgetTypeName())
- .addTaggedData(WIDGET_VERSION, mWidgetVersion)
- .addTaggedData(MODEL_NAME, mModelName)
- .addTaggedData(ENTITY_TYPE, event.mEntityType)
- .addTaggedData(SMART_START, getSmartRangeDelta(mSmartIndices[0]))
- .addTaggedData(SMART_END, getSmartRangeDelta(mSmartIndices[1]))
- .addTaggedData(EVENT_START, getRangeDelta(event.mStart))
- .addTaggedData(EVENT_END, getRangeDelta(event.mEnd))
- .addTaggedData(SESSION_ID, mSessionId);
- mMetricsLogger.write(log);
- debugLog(log);
- mLastEventTime = now;
- mPrevIndices[0] = event.mStart;
- mPrevIndices[1] = event.mEnd;
- mIndex++;
- }
-
- private String startNewSession() {
- endSession();
- mSessionId = createSessionId();
- return mSessionId;
- }
-
- private void endSession() {
- // Reset fields.
- mOrigStart = 0;
- mSmartIndices[0] = mSmartIndices[1] = 0;
- mPrevIndices[0] = mPrevIndices[1] = 0;
- mIndex = 0;
- mSessionStartTime = 0;
- mLastEventTime = 0;
- mSmartSelectionTriggered = false;
- mModelName = getModelName(null);
- mSessionId = null;
- }
-
- private static int getLogType(SelectionEvent event) {
- switch (event.mEventType) {
- case SelectionEvent.ActionType.OVERTYPE:
- return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
- case SelectionEvent.ActionType.COPY:
- return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
- case SelectionEvent.ActionType.PASTE:
- return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
- case SelectionEvent.ActionType.CUT:
- return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
- case SelectionEvent.ActionType.SHARE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
- case SelectionEvent.ActionType.SMART_SHARE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
- case SelectionEvent.ActionType.DRAG:
- return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
- case SelectionEvent.ActionType.ABANDON:
- return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
- case SelectionEvent.ActionType.OTHER:
- return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
- case SelectionEvent.ActionType.SELECT_ALL:
- return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
- case SelectionEvent.ActionType.RESET:
- return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
- case SelectionEvent.EventType.SELECTION_STARTED:
- return MetricsEvent.ACTION_TEXT_SELECTION_START;
- case SelectionEvent.EventType.SELECTION_MODIFIED:
- return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
- case SelectionEvent.EventType.SMART_SELECTION_SINGLE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
- case SelectionEvent.EventType.SMART_SELECTION_MULTI:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
- case SelectionEvent.EventType.AUTO_SELECTION:
- return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
- default:
- return MetricsEvent.VIEW_UNKNOWN;
- }
- }
-
- private static String getLogTypeString(int logType) {
- switch (logType) {
- case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
- return "OVERTYPE";
- case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
- return "COPY";
- case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
- return "PASTE";
- case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
- return "CUT";
- case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
- return "SHARE";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
- return "SMART_SHARE";
- case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
- return "DRAG";
- case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
- return "ABANDON";
- case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
- return "OTHER";
- case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
- return "SELECT_ALL";
- case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
- return "RESET";
- case MetricsEvent.ACTION_TEXT_SELECTION_START:
- return "SELECTION_STARTED";
- case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
- return "SELECTION_MODIFIED";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
- return "SMART_SELECTION_SINGLE";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
- return "SMART_SELECTION_MULTI";
- case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
- return "AUTO_SELECTION";
- default:
- return UNKNOWN;
- }
- }
-
- private int getRangeDelta(int offset) {
- return offset - mOrigStart;
- }
-
- private int getSmartRangeDelta(int offset) {
- return mSmartSelectionTriggered ? getRangeDelta(offset) : 0;
- }
-
- private String getWidgetTypeName() {
- switch (mWidgetType) {
- case WidgetType.TEXTVIEW:
- return TEXTVIEW;
- case WidgetType.WEBVIEW:
- return WEBVIEW;
- case WidgetType.EDITTEXT:
- return EDITTEXT;
- case WidgetType.EDIT_WEBVIEW:
- return EDIT_WEBVIEW;
- case WidgetType.UNSELECTABLE_TEXTVIEW:
- return UNSELECTABLE_TEXTVIEW;
- case WidgetType.CUSTOM_TEXTVIEW:
- return CUSTOM_TEXTVIEW;
- case WidgetType.CUSTOM_EDITTEXT:
- return CUSTOM_EDITTEXT;
- case WidgetType.CUSTOM_UNSELECTABLE_TEXTVIEW:
- return CUSTOM_UNSELECTABLE_TEXTVIEW;
- default:
- return UNKNOWN;
- }
- }
-
- private String getModelName(@Nullable SelectionEvent event) {
- return event == null
- ? SelectionEvent.NO_VERSION_TAG
- : Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
- }
-
- private static String createSessionId() {
- return UUID.randomUUID().toString();
- }
-
- private static void debugLog(LogMaker log) {
- if (!DEBUG_LOG_ENABLED) return;
-
- final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
- final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
- final String widget = widgetVersion.isEmpty()
- ? widgetType : widgetType + "-" + widgetVersion;
- final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
- if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
- String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
- sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
- Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
- }
-
- final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
- final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
- final String type = getLogTypeString(log.getType());
- final int smartStart = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_START), ZERO));
- final int smartEnd = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_END), ZERO));
- final int eventStart = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_START), ZERO));
- final int eventEnd = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_END), ZERO));
-
- Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
- index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
- }
-
- /**
- * A selection event.
- * Specify index parameters as word token indices.
- */
- public static final class SelectionEvent {
-
- /**
- * Use this to specify an indeterminate positive index.
- */
- public static final int OUT_OF_BOUNDS = Integer.MAX_VALUE;
-
- /**
- * Use this to specify an indeterminate negative index.
- */
- public static final int OUT_OF_BOUNDS_NEGATIVE = Integer.MIN_VALUE;
-
- private static final String NO_VERSION_TAG = "";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ActionType.OVERTYPE, ActionType.COPY, ActionType.PASTE, ActionType.CUT,
- ActionType.SHARE, ActionType.SMART_SHARE, ActionType.DRAG, ActionType.ABANDON,
- ActionType.OTHER, ActionType.SELECT_ALL, ActionType.RESET})
- public @interface ActionType {
- /** User typed over the selection. */
- int OVERTYPE = 100;
- /** User copied the selection. */
- int COPY = 101;
- /** User pasted over the selection. */
- int PASTE = 102;
- /** User cut the selection. */
- int CUT = 103;
- /** User shared the selection. */
- int SHARE = 104;
- /** User clicked the textAssist menu item. */
- int SMART_SHARE = 105;
- /** User dragged+dropped the selection. */
- int DRAG = 106;
- /** User abandoned the selection. */
- int ABANDON = 107;
- /** User performed an action on the selection. */
- int OTHER = 108;
-
- /* Non-terminal actions. */
- /** User activated Select All */
- int SELECT_ALL = 200;
- /** User reset the smart selection. */
- int RESET = 201;
- }
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ActionType.OVERTYPE, ActionType.COPY, ActionType.PASTE, ActionType.CUT,
- ActionType.SHARE, ActionType.SMART_SHARE, ActionType.DRAG, ActionType.ABANDON,
- ActionType.OTHER, ActionType.SELECT_ALL, ActionType.RESET,
- EventType.SELECTION_STARTED, EventType.SELECTION_MODIFIED,
- EventType.SMART_SELECTION_SINGLE, EventType.SMART_SELECTION_MULTI,
- EventType.AUTO_SELECTION})
- private @interface EventType {
- /** User started a new selection. */
- int SELECTION_STARTED = 1;
- /** User modified an existing selection. */
- int SELECTION_MODIFIED = 2;
- /** Smart selection triggered for a single token (word). */
- int SMART_SELECTION_SINGLE = 3;
- /** Smart selection triggered spanning multiple tokens (words). */
- int SMART_SELECTION_MULTI = 4;
- /** Something else other than User or the default TextClassifier triggered a selection. */
- int AUTO_SELECTION = 5;
- }
-
- private final int mStart;
- private final int mEnd;
- private @EventType int mEventType;
- private final @TextClassifier.EntityType String mEntityType;
- private final String mVersionTag;
-
- private SelectionEvent(
- int start, int end, int eventType,
- @TextClassifier.EntityType String entityType, String versionTag) {
- Preconditions.checkArgument(end >= start, "end cannot be less than start");
- mStart = start;
- mEnd = end;
- mEventType = eventType;
- mEntityType = Objects.requireNonNull(entityType);
- mVersionTag = Objects.requireNonNull(versionTag);
- }
-
- /**
- * Creates a "selection started" event.
- *
- * @param start the word index of the selected word
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionStarted(int start) {
- return new SelectionEvent(
- start, start + 1, EventType.SELECTION_STARTED,
- TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
- }
-
- /**
- * Creates a "selection modified" event.
- * Use when the user modifies the selection.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionModified(int start, int end) {
- return new SelectionEvent(
- start, end, EventType.SELECTION_MODIFIED,
- TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
- }
-
- /**
- * Creates a "selection modified" event.
- * Use when the user modifies the selection and the selection's entity type is known.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- * @param classification the TextClassification object returned by the TextClassifier that
- * classified the selected text
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionModified(
- int start, int end, @NonNull TextClassification classification) {
- final String entityType = classification.getEntityCount() > 0
- ? classification.getEntity(0)
- : TextClassifier.TYPE_UNKNOWN;
- final String versionTag = getVersionInfo(classification.getId());
- return new SelectionEvent(
- start, end, EventType.SELECTION_MODIFIED, entityType, versionTag);
- }
-
- /**
- * Creates a "selection modified" event.
- * Use when a TextClassifier modifies the selection.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- * @param selection the TextSelection object returned by the TextClassifier for the
- * specified selection
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionModified(
- int start, int end, @NonNull TextSelection selection) {
- final boolean smartSelection = getSourceClassifier(selection.getId())
- .equals(TextClassifier.DEFAULT_LOG_TAG);
- final int eventType;
- if (smartSelection) {
- eventType = end - start > 1
- ? EventType.SMART_SELECTION_MULTI
- : EventType.SMART_SELECTION_SINGLE;
-
- } else {
- eventType = EventType.AUTO_SELECTION;
- }
- final String entityType = selection.getEntityCount() > 0
- ? selection.getEntity(0)
- : TextClassifier.TYPE_UNKNOWN;
- final String versionTag = getVersionInfo(selection.getId());
- return new SelectionEvent(start, end, eventType, entityType, versionTag);
- }
-
- /**
- * Creates an event specifying an action taken on a selection.
- * Use when the user clicks on an action to act on the selected text.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- * @param actionType the action that was performed on the selection
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionAction(
- int start, int end, @ActionType int actionType) {
- return new SelectionEvent(
- start, end, actionType, TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
- }
-
- /**
- * Creates an event specifying an action taken on a selection.
- * Use when the user clicks on an action to act on the selected text and the selection's
- * entity type is known.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- * @param actionType the action that was performed on the selection
- * @param classification the TextClassification object returned by the TextClassifier that
- * classified the selected text
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionAction(
- int start, int end, @ActionType int actionType,
- @NonNull TextClassification classification) {
- final String entityType = classification.getEntityCount() > 0
- ? classification.getEntity(0)
- : TextClassifier.TYPE_UNKNOWN;
- final String versionTag = getVersionInfo(classification.getId());
- return new SelectionEvent(start, end, actionType, entityType, versionTag);
- }
-
- @VisibleForTesting
- public static String getVersionInfo(String signature) {
- final int start = signature.indexOf("|") + 1;
- final int end = signature.indexOf("|", start);
- if (start >= 1 && end >= start) {
- return signature.substring(start, end);
- }
- return "";
- }
-
- private static String getSourceClassifier(String signature) {
- final int end = signature.indexOf("|");
- if (end >= 0) {
- return signature.substring(0, end);
- }
- return "";
- }
-
- private boolean isTerminal() {
- switch (mEventType) {
- case ActionType.OVERTYPE: // fall through
- case ActionType.COPY: // fall through
- case ActionType.PASTE: // fall through
- case ActionType.CUT: // fall through
- case ActionType.SHARE: // fall through
- case ActionType.SMART_SHARE: // fall through
- case ActionType.DRAG: // fall through
- case ActionType.ABANDON: // fall through
- case ActionType.OTHER: // fall through
- return true;
- default:
- return false;
- }
- }
- }
-}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index d165bd0..ffdb89d 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -261,7 +261,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- String tz = intent.getStringExtra("time-zone");
+ String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
mClock = Clock.system(ZoneId.of(tz));
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 816612f..16d9ed3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4589,7 +4589,7 @@
private float mTouchOffsetY;
// Where the touch position should be on the handle to ensure a maximum cursor visibility.
// This is the distance in pixels from the top of the handle view.
- private float mIdealVerticalOffset;
+ private final float mIdealVerticalOffset;
// Parent's (TextView) previous position in window
private int mLastParentX, mLastParentY;
// Parent's (TextView) previous position on screen
@@ -4638,8 +4638,18 @@
final int handleHeight = getPreferredHeight();
mTouchOffsetY = -0.3f * handleHeight;
- mIdealVerticalOffset = 0.7f * handleHeight;
- mIdealFingerToCursorOffset = (int)(mIdealVerticalOffset - mTouchOffsetY);
+ final int distance = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE,
+ WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT);
+ if (distance < 0 || distance > 100) {
+ mIdealVerticalOffset = 0.7f * handleHeight;
+ mIdealFingerToCursorOffset = (int)(mIdealVerticalOffset - mTouchOffsetY);
+ } else {
+ mIdealFingerToCursorOffset = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, distance,
+ mTextView.getContext().getResources().getDisplayMetrics());
+ mIdealVerticalOffset = mIdealFingerToCursorOffset + mTouchOffsetY;
+ }
}
public float getIdealVerticalOffset() {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 01a0e9b..7f6c0d2 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2818,7 +2818,7 @@
/**
* When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
- * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+ * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
* this method should be used to set a single PendingIntent template on the collection, and
* individual items can differentiate their on-click behavior using
* {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
@@ -2834,7 +2834,7 @@
/**
* When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
- * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+ * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
* a single PendingIntent template can be set on the collection, see {@link
* RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
* action of a given item can be distinguished by setting a fillInIntent on that item. The
@@ -3960,7 +3960,7 @@
/**
* When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
- * very costly to set PendingIntents on the individual items, and is hence not permitted.
+ * very costly to set PendingIntents on the individual items, and is hence not recommended.
* Instead a single PendingIntent template can be set on the collection, see {@link
* RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
* action of a given item can be distinguished by setting a fillInIntent on that item. The
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 4ef3f61..45943f5 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -39,7 +39,6 @@
import android.view.textclassifier.ExtrasUtils;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.SelectionEvent.InvocationMethod;
-import android.view.textclassifier.SelectionSessionLogger;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationConstants;
import android.view.textclassifier.TextClassificationContext;
@@ -705,7 +704,7 @@
SelectionMetricsLogger(TextView textView) {
Objects.requireNonNull(textView);
mEditTextLogger = textView.isTextEditable();
- mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
+ mTokenIterator = BreakIterator.getWordInstance(textView.getTextLocale());
}
public void logSelectionStarted(
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 8565493..6432438 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -173,7 +173,7 @@
return; // Test disabled the clock ticks
}
if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
- final String timeZone = intent.getStringExtra("time-zone");
+ final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
createTime(timeZone);
} else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
|| Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0d82065..2168018 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6607,6 +6607,16 @@
return mTransformation instanceof PasswordTransformationMethod;
}
+ /**
+ * Returns true if the current inputType is any type of password.
+ *
+ * @hide
+ */
+ public boolean isAnyPasswordInputType() {
+ final int inputType = getInputType();
+ return isPasswordInputType(inputType) || isVisiblePasswordInputType(inputType);
+ }
+
static boolean isPasswordInputType(int inputType) {
final int variation =
inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index bce5497..09ab5aa 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -41,6 +41,25 @@
public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true;
/**
+ * The flag of finger-to-cursor distance in DP for cursor dragging.
+ * The value unit is DP and the range is {0..100}. If the value is out of range, the legacy
+ * value, which is based on handle size, will be used.
+ */
+ public static final String FINGER_TO_CURSOR_DISTANCE =
+ "CursorControlFeature__finger_to_cursor_distance";
+
+ /**
+ * The key used in app core settings for the flag {@link #FINGER_TO_CURSOR_DISTANCE}.
+ */
+ public static final String KEY_FINGER_TO_CURSOR_DISTANCE =
+ "widget__finger_to_cursor_distance";
+
+ /**
+ * Default value for the flag {@link #FINGER_TO_CURSOR_DISTANCE}.
+ */
+ public static final int FINGER_TO_CURSOR_DISTANCE_DEFAULT = -1;
+
+ /**
* Whether additional gestures should be enabled for the insertion cursor handle (e.g.
* long-press or double-tap on the handle to trigger selection).
*/
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index d50826f..ec2653f 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -26,6 +26,7 @@
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
import android.stats.devicepolicy.DevicePolicyEnums;
@@ -90,7 +91,9 @@
@Override
public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
- userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+ });
mIsWaitingToEnableWorkProfile = true;
}
};
@@ -284,7 +287,7 @@
}
private int userHandleToPageIndex(UserHandle userHandle) {
- if (userHandle == getPersonalListAdapter().mResolverListController.getUserHandle()) {
+ if (userHandle.equals(getPersonalListAdapter().mResolverListController.getUserHandle())) {
return PROFILE_PERSONAL;
} else {
return PROFILE_WORK;
@@ -293,7 +296,7 @@
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
UserHandle listUserHandle = activeListAdapter.getUserHandle();
- if (listUserHandle == mWorkProfileUserHandle
+ if (listUserHandle.equals(mWorkProfileUserHandle)
&& mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
@@ -311,7 +314,7 @@
if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
UserHandle.myUserId(), listUserHandle.getIdentifier())) {
- if (listUserHandle == mPersonalProfileUserHandle) {
+ if (listUserHandle.equals(mPersonalProfileUserHandle)) {
DevicePolicyEventLogger.createEvent(
DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
.setStrings(getMetricsCategory())
@@ -452,7 +455,7 @@
mEmptyStateView = rootView.findViewById(R.id.resolver_empty_state);
}
- private ViewGroup getEmptyStateView() {
+ protected ViewGroup getEmptyStateView() {
return mEmptyStateView;
}
}
diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java
index eb74612..e0bbc04 100644
--- a/core/java/com/android/internal/app/AbstractResolverComparator.java
+++ b/core/java/com/android/internal/app/AbstractResolverComparator.java
@@ -221,6 +221,12 @@
*/
abstract float getScore(ComponentName name);
+ /**
+ * Returns the list of top K component names which have highest
+ * {@link #getScore(ComponentName)}
+ */
+ abstract List<ComponentName> getTopComponentNames(int topK);
+
/** Handles result message sent to mHandler. */
abstract void handleResultMessage(Message message);
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index 04ad7e9..ba8c7b3 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -36,7 +36,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
/**
* Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be
@@ -160,6 +162,15 @@
}
@Override
+ List<ComponentName> getTopComponentNames(int topK) {
+ return mTargetRanks.entrySet().stream()
+ .sorted(Entry.comparingByValue())
+ .limit(topK)
+ .map(Entry::getKey)
+ .collect(Collectors.toList());
+ }
+
+ @Override
void updateModel(ComponentName componentName) {
if (mResolverRankerService != null) {
mResolverRankerService.updateModel(componentName);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index c099301..78a0ae0 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -223,6 +223,11 @@
SystemUiDeviceConfigFlags.HASH_SALT_MAX_DAYS,
DEFAULT_SALT_EXPIRATION_DAYS);
+ private boolean mAppendDirectShareEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
+ false);
+
private Bundle mReplacementExtras;
private IntentSender mChosenComponentSender;
private IntentSender mRefinementIntentSender;
@@ -409,6 +414,11 @@
private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 10000;
private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 3000;
+ private static final int DEFAULT_DIRECT_SHARE_TIMEOUT_MILLIS = 1500;
+ private int mDirectShareTimeout = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.SHARE_SHEET_DIRECT_SHARE_TIMEOUT,
+ DEFAULT_DIRECT_SHARE_TIMEOUT_MILLIS);
+
private boolean mMinTimeoutPassed = false;
private void removeAllMessages() {
@@ -427,15 +437,14 @@
if (DEBUG) {
Log.d(TAG, "queryTargets setting watchdog timer for "
- + WATCHDOG_TIMEOUT_MIN_MILLIS + "-"
+ + mDirectShareTimeout + "-"
+ WATCHDOG_TIMEOUT_MAX_MILLIS + "ms");
}
sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT,
WATCHDOG_TIMEOUT_MIN_MILLIS);
sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT,
- WATCHDOG_TIMEOUT_MAX_MILLIS);
-
+ mAppendDirectShareEnabled ? mDirectShareTimeout : WATCHDOG_TIMEOUT_MAX_MILLIS);
}
private void maybeStopServiceRequestTimer() {
@@ -463,6 +472,7 @@
final ServiceResultInfo sri = (ServiceResultInfo) msg.obj;
if (!mServiceConnections.contains(sri.connection)) {
Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection
+ + sri.originalTarget.getResolveInfo().activityInfo.packageName
+ " returned after being removed from active connections."
+ " Have you considered returning results faster?");
break;
@@ -474,7 +484,7 @@
if (adapterForUserHandle != null) {
adapterForUserHandle.addServiceResults(sri.originalTarget,
sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET,
- /* directShareShortcutInfoCache */ null);
+ /* directShareShortcutInfoCache */ null, mServiceConnections);
}
}
unbindService(sri.connection);
@@ -489,6 +499,7 @@
break;
case CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT:
+ mMinTimeoutPassed = true;
unbindRemainingServices();
maybeStopServiceRequestTimer();
break;
@@ -513,7 +524,7 @@
if (adapterForUserHandle != null) {
adapterForUserHandle.addServiceResults(
resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1,
- mDirectShareShortcutInfoCache);
+ mDirectShareShortcutInfoCache, mServiceConnections);
}
}
break;
@@ -1481,7 +1492,7 @@
/* origTarget */ null,
Lists.newArrayList(mCallerChooserTargets),
TARGET_TYPE_DEFAULT,
- /* directShareShortcutInfoCache */ null);
+ /* directShareShortcutInfoCache */ null, mServiceConnections);
}
}
@@ -1525,10 +1536,12 @@
labels.add(innerInfo.getResolveInfo().loadLabel(getPackageManager()));
}
f = new ResolverTargetActionsDialogFragment(mti.getDisplayLabel(), name,
- mti.getTargets(), labels);
+ mti.getTargets(), labels,
+ mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
} else {
f = new ResolverTargetActionsDialogFragment(
- ti.getResolveInfo().loadLabel(getPackageManager()), name, pinned);
+ ti.getResolveInfo().loadLabel(getPackageManager()), name, pinned,
+ mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
}
f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
@@ -2129,7 +2142,7 @@
return null;
}
- if (getPersonalProfileUserHandle() == userHandle) {
+ if (getPersonalProfileUserHandle().equals(userHandle)) {
if (mPersonalAppPredictor != null) {
return mPersonalAppPredictor;
}
@@ -2155,7 +2168,7 @@
.getSystemService(AppPredictionManager.class);
AppPredictor appPredictionSession = appPredictionManager.createAppPredictionSession(
appPredictionContext);
- if (getPersonalProfileUserHandle() == userHandle) {
+ if (getPersonalProfileUserHandle().equals(userHandle)) {
mPersonalAppPredictor = appPredictionSession;
} else {
mWorkAppPredictor = appPredictionSession;
@@ -2411,6 +2424,8 @@
mChooserMultiProfilePagerAdapter.getActiveAdapterView()
.setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter());
return;
+ } else if (mChooserMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) {
+ return;
}
getMainThreadHandler().post(() -> {
@@ -2455,39 +2470,46 @@
offset += tabDivider.getHeight();
}
- int directShareHeight = 0;
- rowsToShow = Math.min(4, rowsToShow);
- mLastNumberOfChildren = recyclerView.getChildCount();
- for (int i = 0, childCount = recyclerView.getChildCount();
- i < childCount && rowsToShow > 0; i++) {
- View child = recyclerView.getChildAt(i);
- if (((GridLayoutManager.LayoutParams)
- child.getLayoutParams()).getSpanIndex() != 0) {
- continue;
+ if (recyclerView.getVisibility() == View.VISIBLE) {
+ int directShareHeight = 0;
+ rowsToShow = Math.min(4, rowsToShow);
+ mLastNumberOfChildren = recyclerView.getChildCount();
+ for (int i = 0, childCount = recyclerView.getChildCount();
+ i < childCount && rowsToShow > 0; i++) {
+ View child = recyclerView.getChildAt(i);
+ if (((GridLayoutManager.LayoutParams)
+ child.getLayoutParams()).getSpanIndex() != 0) {
+ continue;
+ }
+ int height = child.getHeight();
+ offset += height;
+
+ if (gridAdapter.getTargetType(
+ recyclerView.getChildAdapterPosition(child))
+ == ChooserListAdapter.TARGET_SERVICE) {
+ directShareHeight = height;
+ }
+ rowsToShow--;
}
- int height = child.getHeight();
- offset += height;
- if (gridAdapter.getTargetType(
- recyclerView.getChildAdapterPosition(child))
- == ChooserListAdapter.TARGET_SERVICE) {
- directShareHeight = height;
+ boolean isExpandable = getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode();
+ if (directShareHeight != 0 && isSendAction(getTargetIntent())
+ && isExpandable) {
+ // make sure to leave room for direct share 4->8 expansion
+ int requiredExpansionHeight =
+ (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
+ int topInset = mSystemWindowInsets != null ? mSystemWindowInsets.top : 0;
+ int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight()
+ - requiredExpansionHeight - topInset - bottomInset;
+
+ offset = Math.min(offset, minHeight);
}
- rowsToShow--;
- }
-
- boolean isExpandable = getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode();
- if (directShareHeight != 0 && isSendAction(getTargetIntent())
- && isExpandable) {
- // make sure to leave room for direct share 4->8 expansion
- int requiredExpansionHeight =
- (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
- int topInset = mSystemWindowInsets != null ? mSystemWindowInsets.top : 0;
- int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight()
- - requiredExpansionHeight - topInset - bottomInset;
-
- offset = Math.min(offset, minHeight);
+ } else {
+ ViewGroup currentEmptyStateView = getCurrentEmptyStateView();
+ if (currentEmptyStateView.getVisibility() == View.VISIBLE) {
+ offset += currentEmptyStateView.getHeight();
+ }
}
mResolverDrawerLayout.setCollapsibleHeightReserved(Math.min(offset, bottom - top));
@@ -2495,6 +2517,11 @@
}
}
+ private ViewGroup getCurrentEmptyStateView() {
+ int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage();
+ return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView();
+ }
+
static class BaseChooserTargetComparator implements Comparator<ChooserTarget> {
@Override
public int compare(ChooserTarget lhs, ChooserTarget rhs) {
@@ -2541,7 +2568,7 @@
ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter;
if (chooserListAdapter.getUserHandle()
- == mChooserMultiProfilePagerAdapter.getCurrentUserHandle()) {
+ .equals(mChooserMultiProfilePagerAdapter.getCurrentUserHandle())) {
mChooserMultiProfilePagerAdapter.getActiveAdapterView()
.setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter());
mChooserMultiProfilePagerAdapter
@@ -3570,6 +3597,10 @@
? mOriginalTarget.getResolveInfo().activityInfo.toString()
: "<connection destroyed>") + "}";
}
+
+ public ComponentName getComponentName() {
+ return mOriginalTarget.getResolveInfo().activityInfo.getComponentName();
+ }
}
static class ServiceResultInfo {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 74ae291..0ea855a 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -32,8 +32,10 @@
import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
import android.util.Log;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
@@ -44,17 +46,26 @@
import com.android.internal.app.chooser.MultiDisplayResolveInfo;
import com.android.internal.app.chooser.SelectableTargetInfo;
import com.android.internal.app.chooser.TargetInfo;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
public class ChooserListAdapter extends ResolverListAdapter {
private static final String TAG = "ChooserListAdapter";
private static final boolean DEBUG = false;
+ private boolean mAppendDirectShareEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
+ false);
+
private boolean mEnableStackedApps = true;
public static final int NO_POSITION = -1;
@@ -84,6 +95,11 @@
// Reserve spots for incoming direct share targets by adding placeholders
private ChooserTargetInfo
mPlaceHolderTargetInfo = new ChooserActivity.PlaceHolderTargetInfo();
+ private int mValidServiceTargetsNum = 0;
+ private final Map<ComponentName, Pair<List<ChooserTargetInfo>, Integer>>
+ mParkingDirectShareTargets = new HashMap<>();
+ private Set<ComponentName> mPendingChooserTargetService = new HashSet<>();
+ private Set<ComponentName> mShortcutComponents = new HashSet<>();
private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
private final List<TargetInfo> mCallerTargets = new ArrayList<>();
@@ -189,6 +205,9 @@
void refreshListView() {
if (mListViewDataChanged) {
+ if (mAppendDirectShareEnabled) {
+ appendServiceTargetsWithQuota();
+ }
super.notifyDataSetChanged();
}
mListViewDataChanged = false;
@@ -198,6 +217,10 @@
private void createPlaceHolders() {
mNumShortcutResults = 0;
mServiceTargets.clear();
+ mValidServiceTargetsNum = 0;
+ mParkingDirectShareTargets.clear();
+ mPendingChooserTargetService.clear();
+ mShortcutComponents.clear();
for (int i = 0; i < MAX_SERVICE_TARGETS; i++) {
mServiceTargets.add(mPlaceHolderTargetInfo);
}
@@ -393,12 +416,19 @@
*/
public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets,
@ChooserActivity.ShareTargetType int targetType,
- Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos) {
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos,
+ List<ChooserActivity.ChooserTargetServiceConnection>
+ pendingChooserTargetServiceConnections) {
if (DEBUG) {
- Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
+ Log.d(TAG, "addServiceResults " + origTarget.getResolvedComponentName() + ", "
+ + targets.size()
+ " targets");
}
-
+ if (mAppendDirectShareEnabled) {
+ parkTargetIntoMemory(origTarget, targets, targetType, directShareToShortcutInfos,
+ pendingChooserTargetServiceConnections);
+ return;
+ }
if (targets.size() == 0) {
return;
}
@@ -449,6 +479,126 @@
}
}
+ /**
+ * Park {@code targets} into memory for the moment to surface them later when view is refreshed.
+ * Components pending on ChooserTargetService query are also recorded.
+ */
+ private void parkTargetIntoMemory(DisplayResolveInfo origTarget, List<ChooserTarget> targets,
+ @ChooserActivity.ShareTargetType int targetType,
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos,
+ List<ChooserActivity.ChooserTargetServiceConnection>
+ pendingChooserTargetServiceConnections) {
+ ComponentName origComponentName = origTarget.getResolvedComponentName();
+ mPendingChooserTargetService = pendingChooserTargetServiceConnections.stream()
+ .map(ChooserActivity.ChooserTargetServiceConnection::getComponentName)
+ .filter(componentName -> !componentName.equals(origComponentName))
+ .collect(Collectors.toSet());
+ // Park targets in memory
+ if (!targets.isEmpty() && !mParkingDirectShareTargets.containsKey(origComponentName)) {
+ final boolean isShortcutResult =
+ (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER
+ || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
+ Context contextAsUser = mContext.createContextAsUser(getUserHandle(),
+ 0 /* flags */);
+ List<ChooserTargetInfo> parkingTargetInfos = targets.stream()
+ .map(target ->
+ new SelectableTargetInfo(
+ contextAsUser, origTarget, target, target.getScore(),
+ mSelectableTargetInfoComunicator,
+ (isShortcutResult ? directShareToShortcutInfos.get(target)
+ : null))
+ )
+ .collect(Collectors.toList());
+ mParkingDirectShareTargets.put(origComponentName,
+ new Pair<>(parkingTargetInfos, 0));
+ if (isShortcutResult) {
+ mShortcutComponents.add(origComponentName);
+ }
+ }
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Append targets of top ranked share app into direct share row with quota limit. Remove
+ * appended ones from memory.
+ */
+ private void appendServiceTargetsWithQuota() {
+ int maxRankedTargets = mChooserListCommunicator.getMaxRankedTargets();
+ List<ComponentName> topComponentNames = getTopComponentNames(maxRankedTargets);
+ int appRank = 0;
+ for (ComponentName component : topComponentNames) {
+ if (!mPendingChooserTargetService.contains(component)
+ && !mParkingDirectShareTargets.containsKey(component)) {
+ continue;
+ }
+ appRank++;
+ Pair<List<ChooserTargetInfo>, Integer> parkingTargetsItem =
+ mParkingDirectShareTargets.get(component);
+ if (parkingTargetsItem != null && parkingTargetsItem.second == 0) {
+ List<ChooserTargetInfo> parkingTargets = parkingTargetsItem.first;
+ int initTargetsQuota = appRank <= maxRankedTargets / 2 ? 2 : 1;
+ int insertedNum = 0;
+ while (insertedNum < initTargetsQuota && !parkingTargets.isEmpty()) {
+ if (!checkDuplicateTarget(parkingTargets.get(0))) {
+ mServiceTargets.add(mValidServiceTargetsNum, parkingTargets.get(0));
+ mValidServiceTargetsNum++;
+ insertedNum++;
+ }
+ parkingTargets.remove(0);
+ }
+ mParkingDirectShareTargets.put(component, new Pair<>(parkingTargets, insertedNum));
+ if (mShortcutComponents.contains(component)) {
+ mNumShortcutResults += insertedNum;
+ }
+ }
+ }
+ }
+
+ /**
+ * Append all remaining targets (parking in memory) into direct share row as per their ranking.
+ */
+ private void fillAllServiceTargets() {
+ int maxRankedTargets = mChooserListCommunicator.getMaxRankedTargets();
+ List<ComponentName> topComponentNames = getTopComponentNames(maxRankedTargets);
+ // Append all remaining targets of top recommended components into direct share row.
+ for (ComponentName component : topComponentNames) {
+ if (!mParkingDirectShareTargets.containsKey(component)) {
+ continue;
+ }
+ mParkingDirectShareTargets.get(component).first.stream()
+ .filter(target -> !checkDuplicateTarget(target))
+ .forEach(target -> {
+ if (mShortcutComponents.contains(component)) {
+ mNumShortcutResults++;
+ }
+ mServiceTargets.add(target);
+ });
+ mParkingDirectShareTargets.remove(component);
+ }
+ // Append all remaining shortcuts targets into direct share row.
+ List<ChooserTargetInfo> remainingTargets = new ArrayList<>();
+ mParkingDirectShareTargets.entrySet().stream()
+ .filter(entry -> mShortcutComponents.contains(entry.getKey()))
+ .map(entry -> entry.getValue())
+ .map(pair -> pair.first)
+ .forEach(remainingTargets::addAll);
+ remainingTargets.sort(
+ (t1, t2) -> -Float.compare(t1.getModifiedScore(), t2.getModifiedScore()));
+ mServiceTargets.addAll(remainingTargets);
+ mNumShortcutResults += remainingTargets.size();
+ mParkingDirectShareTargets.clear();
+ }
+
+ private boolean checkDuplicateTarget(ChooserTargetInfo chooserTargetInfo) {
+ // Check for duplicates and abort if found
+ for (ChooserTargetInfo otherTargetInfo : mServiceTargets) {
+ if (chooserTargetInfo.isSimilar(otherTargetInfo)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
int getNumShortcutResults() {
return mNumShortcutResults;
}
@@ -487,7 +637,9 @@
*/
public void completeServiceTargetLoading() {
mServiceTargets.removeIf(o -> o instanceof ChooserActivity.PlaceHolderTargetInfo);
-
+ if (mAppendDirectShareEnabled) {
+ fillAllServiceTargets();
+ }
if (mServiceTargets.isEmpty()) {
mServiceTargets.add(new ChooserActivity.EmptyTargetInfo());
}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 2167b1e..c6a00f3 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -93,10 +93,10 @@
@Override
@Nullable
ChooserListAdapter getListAdapterForUserHandle(UserHandle userHandle) {
- if (getActiveListAdapter().getUserHandle() == userHandle) {
+ if (getActiveListAdapter().getUserHandle().equals(userHandle)) {
return getActiveListAdapter();
} else if (getInactiveListAdapter() != null
- && getInactiveListAdapter().getUserHandle() == userHandle) {
+ && getInactiveListAdapter().getUserHandle().equals(userHandle)) {
return getInactiveListAdapter();
}
return null;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 907ea55..9218823 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -34,14 +34,14 @@
// be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
// and not be reordered
int checkOperation(int code, int uid, String packageName);
- int noteOperation(int code, int uid, String packageName, @nullable String featureId,
+ int noteOperation(int code, int uid, String packageName, @nullable String attributionTag,
boolean shouldCollectAsyncNotedOp, String message);
int startOperation(IBinder clientId, int code, int uid, String packageName,
- @nullable String featureId, boolean startIfModeDefault,
+ @nullable String attributionTag, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message);
@UnsupportedAppUsage
void finishOperation(IBinder clientId, int code, int uid, String packageName,
- @nullable String featureId);
+ @nullable String attributionTag);
void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
void stopWatchingMode(IAppOpsCallback callback);
int permissionToOpCode(String permission);
@@ -52,8 +52,8 @@
// Any new method exposed to native must be added after the last one, do not reorder
int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
- String proxiedFeatureId, int proxyUid, String proxyPackageName,
- String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message);
+ String proxiedAttributionTag, int proxyUid, String proxyPackageName,
+ String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message);
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);
@@ -64,10 +64,10 @@
List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
@UnsupportedAppUsage
List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
- void getHistoricalOps(int uid, String packageName, String featureId, in List<String> ops,
+ void getHistoricalOps(int uid, String packageName, String attributionTag, in List<String> ops,
int filter, long beginTimeMillis, long endTimeMillis, int flags,
in RemoteCallback callback);
- void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId,
+ void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
in List<String> ops, int filter, long beginTimeMillis, long endTimeMillis, int flags,
in RemoteCallback callback);
void offsetHistory(long duration);
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 7a0afa2..9bdfa4a 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.Slog;
import android.widget.Toast;
@@ -153,6 +154,9 @@
}
private boolean shouldShowDisclosure(@Nullable ResolveInfo ri, Intent intent) {
+ if (!isDeviceProvisioned()) {
+ return false;
+ }
if (ri == null || ri.activityInfo == null) {
return true;
}
@@ -163,6 +167,11 @@
return !isTargetResolverOrChooserActivity(ri.activityInfo);
}
+ private boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, /* def= */ 0) != 0;
+ }
+
private boolean isTextMessageIntent(Intent intent) {
return (Intent.ACTION_SENDTO.equals(intent.getAction()) || isViewActionIntent(intent))
&& ALLOWED_TEXT_MESSAGE_SCHEMES.contains(intent.getScheme());
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ec371d9..8e64b97 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -49,6 +49,7 @@
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Insets;
import android.net.Uri;
import android.os.Build;
@@ -65,6 +66,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -1242,12 +1244,12 @@
}
private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
- if (!hasWorkProfile() || currentUserHandle == getUser()) {
+ if (!hasWorkProfile() || currentUserHandle.equals(getUser())) {
return;
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
- .setBoolean(currentUserHandle == getPersonalProfileUserHandle())
+ .setBoolean(currentUserHandle.equals(getPersonalProfileUserHandle()))
.setStrings(getMetricsCategory(),
cti instanceof ChooserTargetInfo ? "direct_share" : "other_target")
.write();
@@ -1303,7 +1305,7 @@
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- startActivity(in);
+ startActivityAsUser(in, mMultiProfilePagerAdapter.getCurrentUserHandle());
}
@VisibleForTesting
@@ -1486,7 +1488,8 @@
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
- .setBoolean(activeListAdapter.getUserHandle() == getPersonalProfileUserHandle())
+ .setBoolean(activeListAdapter.getUserHandle()
+ .equals(getPersonalProfileUserHandle()))
.setStrings(getMetricsCategory())
.write();
safelyStartActivity(activeProfileTarget);
@@ -1605,7 +1608,10 @@
for (int i = 0; i < tabWidget.getChildCount(); i++) {
View tabView = tabWidget.getChildAt(i);
TextView title = tabView.findViewById(android.R.id.title);
- title.setTextColor(getColor(R.color.resolver_tabs_inactive_color));
+ title.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_DialogWindowTitle);
+ title.setTextColor(getAttrColor(this, android.R.attr.textColorTertiary));
+ title.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ getResources().getDimension(R.dimen.resolver_tab_text_size));
if (title.getText().equals(getString(R.string.resolver_personal_tab))) {
tabView.setContentDescription(personalContentDescription);
} else if (title.getText().equals(getString(R.string.resolver_work_tab))) {
@@ -1614,10 +1620,17 @@
}
}
+ private static int getAttrColor(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+ int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ return colorAccent;
+ }
+
private void updateActiveTabStyle(TabHost tabHost) {
TextView title = tabHost.getTabWidget().getChildAt(tabHost.getCurrentTab())
.findViewById(android.R.id.title);
- title.setTextColor(getColor(R.color.resolver_tabs_active_color));
+ title.setTextColor(getAttrColor(this, android.R.attr.colorAccent));
}
private void setupViewVisibilities() {
@@ -1778,7 +1791,7 @@
@Override // ResolverListCommunicator
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
- if (listAdapter.getUserHandle() == getWorkProfileUserHandle()
+ if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
&& mMultiProfilePagerAdapter.isWaitingToEnableWorkProfile()) {
// We have just turned on the work profile and entered the pass code to start it,
// now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
@@ -1819,7 +1832,7 @@
mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
}
if (mMultiProfilePagerAdapter.getCurrentUserHandle()
- == getWorkProfileUserHandle()) {
+ .equals(getWorkProfileUserHandle())) {
mMultiProfilePagerAdapter.rebuildActiveTab(true);
} else {
mMultiProfilePagerAdapter.clearInactiveProfileCache();
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 61a404e..579abee 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -153,6 +153,14 @@
return mResolverListController.getScore(target);
}
+ /**
+ * Returns the list of top K component names which have highest
+ * {@link #getScore(DisplayResolveInfo)}
+ */
+ public List<ComponentName> getTopComponentNames(int topK) {
+ return mResolverListController.getTopComponentNames(topK);
+ }
+
public void updateModel(ComponentName componentName) {
mResolverListController.updateModel(componentName);
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 022aded..51dce55 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -367,6 +367,14 @@
return mResolverComparator.getScore(target.getResolvedComponentName());
}
+ /**
+ * Returns the list of top K component names which have highest
+ * {@link #getScore(DisplayResolveInfo)}
+ */
+ public List<ComponentName> getTopComponentNames(int topK) {
+ return mResolverComparator.getTopComponentNames(topK);
+ }
+
public void updateModel(ComponentName componentName) {
mResolverComparator.updateModel(componentName);
}
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 0440f5e..578f6ae 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -103,10 +103,10 @@
@Override
@Nullable
ResolverListAdapter getListAdapterForUserHandle(UserHandle userHandle) {
- if (getActiveListAdapter().getUserHandle() == userHandle) {
+ if (getActiveListAdapter().getUserHandle().equals(userHandle)) {
return getActiveListAdapter();
} else if (getInactiveListAdapter() != null
- && getInactiveListAdapter().getUserHandle() == userHandle) {
+ && getInactiveListAdapter().getUserHandle().equals(userHandle)) {
return getInactiveListAdapter();
}
return null;
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index 01e0622..2869450 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -48,6 +48,7 @@
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* Ranks and compares packages based on usage stats and uses the {@link ResolverRankerService}.
@@ -252,6 +253,15 @@
return 0;
}
+ @Override
+ List<ComponentName> getTopComponentNames(int topK) {
+ return mTargetsDict.entrySet().stream()
+ .sorted((o1, o2) -> -Float.compare(getScore(o1.getKey()), getScore(o2.getKey())))
+ .limit(topK)
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toList());
+ }
+
// update ranking model when the connection to it is valid.
@Override
public void updateModel(ComponentName componentName) {
diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
index 21efc78..35d9bcd 100644
--- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
@@ -27,6 +27,7 @@
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.Settings;
import com.android.internal.R;
@@ -43,6 +44,7 @@
private static final String NAME_KEY = "componentName";
private static final String TITLE_KEY = "title";
private static final String PINNED_KEY = "pinned";
+ private static final String USER_ID_KEY = "userId";
// Sync with R.array.resolver_target_actions_* resources
private static final int TOGGLE_PIN_INDEX = 0;
@@ -56,19 +58,21 @@
}
public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
- boolean pinned) {
+ boolean pinned, UserHandle userHandle) {
Bundle args = new Bundle();
args.putCharSequence(TITLE_KEY, title);
args.putParcelable(NAME_KEY, name);
args.putBoolean(PINNED_KEY, pinned);
+ args.putParcelable(USER_ID_KEY, userHandle);
setArguments(args);
}
public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
- List<DisplayResolveInfo> targets, List<CharSequence> labels) {
+ List<DisplayResolveInfo> targets, List<CharSequence> labels, UserHandle userHandle) {
Bundle args = new Bundle();
args.putCharSequence(TITLE_KEY, title);
args.putParcelable(NAME_KEY, name);
+ args.putParcelable(USER_ID_KEY, userHandle);
mTargetInfos = targets;
mLabels = labels;
setArguments(args);
@@ -122,7 +126,8 @@
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", name.getPackageName(), null))
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- startActivity(in);
+ UserHandle userHandle = args.getParcelable(USER_ID_KEY);
+ getActivity().startActivityAsUser(in, userHandle);
}
dismiss();
}
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index ab890d2..9ba0259 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -93,6 +93,43 @@
dest.writeString(mDescription);
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("CompatibilityChangeInfo(")
+ .append(getId());
+ if (getName() != null) {
+ sb.append("; name=").append(getName());
+ }
+ if (getEnableAfterTargetSdk() != -1) {
+ sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
+ }
+ if (getDisabled()) {
+ sb.append("; disabled");
+ }
+ if (getLoggingOnly()) {
+ sb.append("; loggingOnly");
+ }
+ return sb.append(")").toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || !(o instanceof CompatibilityChangeInfo)) {
+ return false;
+ }
+ CompatibilityChangeInfo that = (CompatibilityChangeInfo) o;
+ return this.mChangeId == that.mChangeId
+ && this.mName.equals(that.mName)
+ && this.mEnableAfterTargetSdk == that.mEnableAfterTargetSdk
+ && this.mDisabled == that.mDisabled
+ && this.mLoggingOnly == that.mLoggingOnly
+ && this.mDescription.equals(that.mDescription);
+
+ }
+
public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
new Parcelable.Creator<CompatibilityChangeInfo>() {
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 523ed6f..6408def 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -222,6 +222,14 @@
CompatibilityChangeInfo[] listAllChanges();
/**
+ * List the compatibility changes that should be present in the UI.
+ * Filters out certain changes like e.g. logging only.
+ *
+ * @return An array of {@link CompatChangeInfo}.
+ */
+ CompatibilityChangeInfo[] listUIChanges();
+
+ /**
* Get an instance that can determine whether a changeid can be overridden for a package name.
*/
IOverrideValidator getOverrideValidator();
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 086b9d8..837cc46 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -378,6 +378,17 @@
"nav_bar_handle_show_over_lockscreen";
/**
+ * (int) Timeout threshold, in millisecond, that Sharesheet waits for direct share targets.
+ */
+ public static final String SHARE_SHEET_DIRECT_SHARE_TIMEOUT =
+ "share_sheet_direct_share_timeout";
+
+ /**
+ * (boolean) Whether append direct share on Sharesheet is enabled.
+ */
+ public static final String APPEND_DIRECT_SHARE_ENABLED = "append_direct_share_enabled";
+
+ /**
* (boolean) Whether to enable user-drag resizing for PIP.
*/
public static final String PIP_USER_RESIZE = "pip_user_resize";
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 73ef8c6..2f048c9 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -430,7 +430,7 @@
if (shouldHide(file)) continue;
if (file.isDirectory()) {
- for (File child : file.listFiles()) {
+ for (File child : FileUtils.listFilesOrEmpty(file)) {
pending.add(child);
}
}
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 91ef0b5..1296ddc 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -116,6 +116,7 @@
NETWORK_STATUS,
context.getString(R.string.notification_channel_network_status),
NotificationManager.IMPORTANCE_LOW);
+ network.setBlockableSystem(true);
channelsList.add(network);
final NotificationChannel networkAlertsChannel = new NotificationChannel(
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index d92f725b..3407670 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -25,6 +25,7 @@
import com.android.internal.util.Preconditions;
import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -276,7 +277,7 @@
}
threadCpuUsages.add(threadCpuUsage);
}
- } catch (IOException e) {
+ } catch (IOException | DirectoryIteratorException e) {
// Expected when a process finishes
return null;
}
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java b/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
index ffdc33c..c11b939 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Slog;
@@ -99,7 +101,7 @@
@VisibleForTesting
public KernelCpuThreadReaderDiff(KernelCpuThreadReader reader, int minimumTotalCpuUsageMillis) {
- mReader = reader;
+ mReader = checkNotNull(reader);
mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
mPreviousCpuUsage = null;
}
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
index fdcc8a8..c908b8c 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
@@ -95,8 +95,10 @@
KernelCpuThreadReader.create(
NUM_BUCKETS_DEFAULT, UidPredicate.fromString(COLLECTED_UIDS_DEFAULT));
mKernelCpuThreadReaderDiff =
- new KernelCpuThreadReaderDiff(
- mKernelCpuThreadReader, MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
+ mKernelCpuThreadReader == null
+ ? null
+ : new KernelCpuThreadReaderDiff(
+ mKernelCpuThreadReader, MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
}
@Override
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 94924a5..ff03f1a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -140,6 +140,33 @@
*/
public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19;
+ /**
+ * A two-bit field for GWP-ASan level of this process. See the possible values below.
+ */
+ public static final int GWP_ASAN_LEVEL_MASK = (1 << 21) | (1 << 22);
+
+ /**
+ * Disable GWP-ASan in this process.
+ * GWP-ASan is a low-overhead memory bug detector using guard pages on a small
+ * subset of heap allocations.
+ */
+ public static final int GWP_ASAN_LEVEL_NEVER = 0 << 21;
+
+ /**
+ * Enable GWP-ASan in this process with a small sampling rate.
+ * With approx. 1% chance GWP-ASan will be activated and apply its protection
+ * to a small subset of heap allocations.
+ * Otherwise (~99% chance) this process is unaffected.
+ */
+ public static final int GWP_ASAN_LEVEL_LOTTERY = 1 << 21;
+
+ /**
+ * Always enable GWP-ASan in this process.
+ * GWP-ASan is activated unconditionally (but still, only a small subset of
+ * allocations is protected).
+ */
+ public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
@@ -178,6 +205,9 @@
/** List of packages with the same uid, and its app data info: volume uuid and inode. */
public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
+ /** Bind mount app storage dirs to lower fs not via fuse */
+ public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
+
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying a name
* in the abstract socket namespace. This socket name is what the new child zygote
@@ -283,6 +313,7 @@
* @param isTopApp true if the process is for top (high priority) application.
* @param pkgDataInfoList A list that stores related packages and its app data
* info: volume uuid and inode.
+ * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
@@ -290,13 +321,13 @@
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- boolean isTopApp, String[] pkgDataInfoList) {
+ boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList);
+ pkgDataInfoList, bindMountAppStorageDirs);
if (pid == 0) {
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -312,7 +343,8 @@
private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
- String appDataDir, boolean isTopApp, String[] pkgDataInfoList);
+ String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
+ boolean bindMountAppStorageDirs);
/**
* Specialize an unspecialized app process. The current VM must have been started
@@ -339,14 +371,15 @@
* volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
* app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
* app_b_ce_inode, ...];
+ * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*/
private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList) {
+ String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList);
+ pkgDataInfoList, bindMountAppStorageDirs);
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -366,7 +399,7 @@
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList);
+ String[] pkgDataInfoList, boolean bindMountAppStorageDirs);
/**
* Called to do any initialization before starting an application.
@@ -691,7 +724,7 @@
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
- args.mPkgDataInfoList);
+ args.mPkgDataInfoList, args.mBindMountAppStorageDirs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 37f570b..1a63765 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,6 +227,11 @@
String[] mPkgDataInfoList;
/**
+ * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
+ */
+ boolean mBindMountAppStorageDirs;
+
+ /**
* Constructs instance and parses args
*
* @param args zygote command-line args
@@ -447,6 +452,8 @@
}
} else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
mPkgDataInfoList = getAssignmentList(arg);
+ } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
+ mBindMountAppStorageDirs = true;
} else {
break;
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4949811..bc8dfd4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -258,7 +258,7 @@
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
- parsedArgs.mPkgDataInfoList);
+ parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs);
try {
if (pid == 0) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 54cf693..ec1f516 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -125,12 +125,6 @@
private static boolean sPreloadComplete;
- /**
- * Cached classloader to use for the system server. Will only be populated in the system
- * server process.
- */
- private static ClassLoader sCachedSystemServerClassLoader = null;
-
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
bootTimingsTraceLog.traceBegin("BeginPreload");
@@ -508,13 +502,7 @@
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
- if (performSystemServerDexOpt(systemServerClasspath)) {
- // Throw away the cached classloader. If we compiled here, the classloader would
- // not have had AoT-ed artifacts.
- // Note: This only works in a very special environment where selinux enforcement is
- // disabled, e.g., Mac builds.
- sCachedSystemServerClassLoader = null;
- }
+ performSystemServerDexOpt(systemServerClasspath);
// Capturing profiles is only supported for debug or eng builds since selinux normally
// prevents it.
if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
@@ -546,9 +534,10 @@
throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
} else {
- createSystemServerClassLoader();
- ClassLoader cl = sCachedSystemServerClassLoader;
- if (cl != null) {
+ ClassLoader cl = null;
+ if (systemServerClasspath != null) {
+ cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);
+
Thread.currentThread().setContextClassLoader(cl);
}
@@ -564,24 +553,6 @@
}
/**
- * Create the classloader for the system server and store it in
- * {@link sCachedSystemServerClassLoader}. This function may be called through JNI in
- * system server startup, when the runtime is in a critically low state. Do not do
- * extended computation etc here.
- */
- private static void createSystemServerClassLoader() {
- if (sCachedSystemServerClassLoader != null) {
- return;
- }
- final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
- // TODO: Should we run optimization here?
- if (systemServerClasspath != null) {
- sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath,
- VMRuntime.SDK_VERSION_CUR_DEVELOPMENT);
- }
- }
-
- /**
* Note that preparing the profiles for system server does not require special selinux
* permissions. From the installer perspective the system server is a regular package which can
* capture profile information.
@@ -645,16 +616,15 @@
/**
* Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction
- * set of the current runtime. If something was compiled, return true.
+ * set of the current runtime.
*/
- private static boolean performSystemServerDexOpt(String classPath) {
+ private static void performSystemServerDexOpt(String classPath) {
final String[] classPathElements = classPath.split(":");
final IInstalld installd = IInstalld.Stub
.asInterface(ServiceManager.getService("installd"));
final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
String classPathForElement = "";
- boolean compiledSomething = false;
for (String classPathElement : classPathElements) {
// We default to the verify filter because the compilation will happen on /data and
// system server cannot load executable code outside /system.
@@ -695,7 +665,6 @@
uuid, classLoaderContext, seInfo, false /* downgrade */,
targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
"server-dexopt");
- compiledSomething = true;
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -706,8 +675,6 @@
classPathForElement = encodeSystemServerClassPath(
classPathForElement, classPathElement);
}
-
- return compiledSomething;
}
/**
@@ -794,6 +761,10 @@
* this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */
parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ /* Enable gwp-asan on the system server with a small probability. This is the same
+ * policy as applied to native processes and system apps. */
+ parsedArgs.mRuntimeFlags |= Zygote.GWP_ASAN_LEVEL_LOTTERY;
+
if (shouldProfileSystemServer()) {
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
new file mode 100644
index 0000000..ebfea450
--- /dev/null
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+/**
+ * @hide
+ */
+public class GestureNavigationSettingsObserver extends ContentObserver {
+ private Context mContext;
+ private Runnable mOnChangeRunnable;
+
+ public GestureNavigationSettingsObserver(Handler handler, Context context,
+ Runnable onChangeRunnable) {
+ super(handler);
+ mContext = context;
+ mOnChangeRunnable = onChangeRunnable;
+ }
+
+ public void register() {
+ ContentResolver r = mContext.getContentResolver();
+ r.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
+ false, this, UserHandle.USER_ALL);
+ r.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
+ false, this, UserHandle.USER_ALL);
+ }
+
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ if (mOnChangeRunnable != null) {
+ mOnChangeRunnable.run();
+ }
+ }
+
+ public int getLeftSensitivity(Resources userRes) {
+ return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT);
+ }
+
+ public int getRightSensitivity(Resources userRes) {
+ return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT);
+ }
+
+ private int getSensitivity(Resources userRes, String side) {
+ final int inset = userRes.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+ final float scale = Settings.Secure.getFloatForUser(
+ mContext.getContentResolver(), side, 1.0f, UserHandle.USER_CURRENT);
+ return (int) (inset * scale);
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 380a20c..5b79b18 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -136,7 +136,8 @@
// Used to show the authentication dialog (Biometrics, Device Credential)
void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int biometricModality, boolean requireConfirmation, int userId, String opPackageName);
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
+ long operationId);
// Used to notify the authentication dialog that a biometric has been authenticated
void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9907b99..1dbd69c 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -105,7 +105,8 @@
// Used to show the authentication dialog (Biometrics, Device Credential)
void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int biometricModality, boolean requireConfirmation, int userId, String opPackageName);
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
+ long operationId);
// Used to notify the authentication dialog that a biometric has been authenticated
void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 9b2bcfb..488b18d 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -16,6 +16,8 @@
package com.android.internal.util;
+import static java.util.Collections.emptySet;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
@@ -67,7 +69,7 @@
*/
public static @NonNull <T> Set<T> filter(@Nullable Set<T> set,
java.util.function.Predicate<? super T> predicate) {
- if (set == null || set.size() == 0) return Collections.emptySet();
+ if (set == null || set.size() == 0) return emptySet();
ArraySet<T> result = null;
if (set instanceof ArraySet) {
ArraySet<T> arraySet = (ArraySet<T>) set;
@@ -123,7 +125,7 @@
*/
public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur,
Function<? super I, ? extends O> f) {
- if (isEmpty(cur)) return Collections.emptySet();
+ if (isEmpty(cur)) return emptySet();
ArraySet<O> result = new ArraySet<>();
if (cur instanceof ArraySet) {
ArraySet<I> arraySet = (ArraySet<I>) cur;
@@ -182,7 +184,7 @@
* @see Collections#emptySet
*/
public static @NonNull <T> Set<T> emptyIfNull(@Nullable Set<T> cur) {
- return cur == null ? Collections.emptySet() : cur;
+ return cur == null ? emptySet() : cur;
}
/**
@@ -325,9 +327,9 @@
*/
public static @NonNull <T> Set<T> addAll(@Nullable Set<T> cur, @Nullable Collection<T> val) {
if (isEmpty(val)) {
- return cur != null ? cur : Collections.emptySet();
+ return cur != null ? cur : emptySet();
}
- if (cur == null || cur == Collections.emptySet()) {
+ if (cur == null || cur == emptySet()) {
cur = new ArraySet<>();
}
cur.addAll(val);
@@ -338,7 +340,7 @@
* @see #add(List, Object)
*/
public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) {
- if (cur == null || cur == Collections.emptySet()) {
+ if (cur == null || cur == emptySet()) {
cur = new ArraySet<>();
}
cur.add(val);
@@ -390,7 +392,14 @@
* @return a list that will not be affected by mutations to the given original list.
*/
public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) {
- return isEmpty(cur) ? Collections.emptySet() : new ArraySet<>(cur);
+ return isEmpty(cur) ? emptySet() : new ArraySet<>(cur);
+ }
+
+ /**
+ * @return a {@link Set} representing the given collection.
+ */
+ public static @NonNull <T> Set<T> toSet(@Nullable Collection<T> cur) {
+ return isEmpty(cur) ? emptySet() : new ArraySet<>(cur);
}
/**
diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java
index 7f567b9..dd64c40 100644
--- a/core/java/com/android/internal/util/Parcelling.java
+++ b/core/java/com/android/internal/util/Parcelling.java
@@ -221,6 +221,33 @@
}
}
+ class ForInternedStringArraySet implements Parcelling<ArraySet<String>> {
+ @Override
+ public void parcel(ArraySet<String> item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(item.size());
+ for (String string : item) {
+ dest.writeString(string);
+ }
+ }
+ }
+
+ @Override
+ public ArraySet<String> unparcel(Parcel source) {
+ final int size = source.readInt();
+ if (size < 0) {
+ return null;
+ }
+ ArraySet<String> set = new ArraySet<>();
+ for (int count = 0; count < size; count++) {
+ set.add(TextUtils.safeIntern(source.readString()));
+ }
+ return set;
+ }
+ }
+
class ForBoolean implements Parcelling<Boolean> {
@Override
public void parcel(@Nullable Boolean item, Parcel dest, int parcelFlags) {
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 8446bbd..e4a4408 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -200,8 +200,9 @@
try {
return doInvoke();
} finally {
- if (isRecycleOnUse()) doRecycle();
- if (!isRecycled()) {
+ if (isRecycleOnUse()) {
+ doRecycle();
+ } else if (!isRecycled()) {
int argsSize = ArrayUtils.size(mArgs);
for (int i = 0; i < argsSize; i++) {
popArg(i);
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 5dd3389b..47f094f 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -116,7 +116,8 @@
}
@Override
- public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync) {
+ public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom,
+ boolean sync) {
if (sync) {
try {
mSession.wallpaperOffsetsComplete(asBinder());
diff --git a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
index 29bdf56..feb3f02 100644
--- a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
+++ b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
@@ -24,4 +24,6 @@
*/
oneway interface IInlineContentCallback {
void onContent(in SurfaceControlViewHost.SurfacePackage content);
+ void onClick();
+ void onLongClick();
}
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 74ad815..bd0623e 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -32,6 +32,7 @@
import android.widget.RemoteViews;
import java.util.Objects;
+import java.util.function.Consumer;
/**
* An ImageView for displaying an Icon. Avoids reloading the Icon when possible.
@@ -44,6 +45,7 @@
private boolean mInternalSetDrawable;
private boolean mForceHidden;
private int mDesiredVisibility;
+ private Consumer<Integer> mOnVisibilityChangedListener;
@UnsupportedAppUsage
public CachingIconView(Context context, @Nullable AttributeSet attrs) {
@@ -198,6 +200,13 @@
private void updateVisibility() {
int visibility = mDesiredVisibility == VISIBLE && mForceHidden ? INVISIBLE
: mDesiredVisibility;
+ if (mOnVisibilityChangedListener != null) {
+ mOnVisibilityChangedListener.accept(visibility);
+ }
super.setVisibility(visibility);
}
+
+ public void setOnVisibilityChangedListener(Consumer<Integer> listener) {
+ mOnVisibilityChangedListener = listener;
+ }
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
new file mode 100644
index 0000000..73da600
--- /dev/null
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -0,0 +1,909 @@
+/*
+ * 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.internal.widget;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.app.Person;
+import android.app.RemoteInputHistoryItem;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.RemotableViewMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.regex.Pattern;
+
+/**
+ * A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal
+ * messages and adapts the layout accordingly.
+ */
+@RemoteViews.RemoteView
+public class ConversationLayout extends FrameLayout
+ implements ImageMessageConsumer, IMessagingLayout {
+
+ public static final boolean CONVERSATION_LAYOUT_ENABLED = true;
+ private static final float COLOR_SHIFT_AMOUNT = 60;
+ /**
+ * Pattren for filter some ingonable characters.
+ * p{Z} for any kind of whitespace or invisible separator.
+ * p{C} for any kind of punctuation character.
+ */
+ private static final Pattern IGNORABLE_CHAR_PATTERN
+ = Pattern.compile("[\\p{C}\\p{Z}]");
+ private static final Pattern SPECIAL_CHAR_PATTERN
+ = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
+ private static final Consumer<MessagingMessage> REMOVE_MESSAGE
+ = MessagingMessage::removeMessage;
+ public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+ public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+ public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ public static final OnLayoutChangeListener MESSAGING_PROPERTY_ANIMATOR
+ = new MessagingPropertyAnimator();
+ private List<MessagingMessage> mMessages = new ArrayList<>();
+ private List<MessagingMessage> mHistoricMessages = new ArrayList<>();
+ private MessagingLinearLayout mMessagingLinearLayout;
+ private boolean mShowHistoricMessages;
+ private ArrayList<MessagingGroup> mGroups = new ArrayList<>();
+ private TextView mTitleView;
+ private int mLayoutColor;
+ private int mSenderTextColor;
+ private int mMessageTextColor;
+ private int mAvatarSize;
+ private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private Paint mTextPaint = new Paint();
+ private Icon mAvatarReplacement;
+ private boolean mIsOneToOne;
+ private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
+ private Person mUser;
+ private CharSequence mNameReplacement;
+ private boolean mIsCollapsed;
+ private ImageResolver mImageResolver;
+ private ImageView mConversationIcon;
+ private TextView mConversationText;
+ private View mConversationIconBadge;
+ private Icon mLargeIcon;
+ private View mExpandButtonContainer;
+ private ViewGroup mExpandButtonAndContentContainer;
+ private NotificationExpandButton mExpandButton;
+ private int mExpandButtonExpandedTopMargin;
+ private int mBadgedSideMargins;
+ private int mIconSizeBadged;
+ private int mIconSizeCentered;
+ private CachingIconView mIcon;
+ private int mExpandedGroupTopMargin;
+ private int mExpandButtonExpandedSize;
+ private View mConversationFacePile;
+ private int mNotificationBackgroundColor;
+ private CharSequence mFallbackChatName;
+ private CharSequence mFallbackGroupChatName;
+ private CharSequence mConversationTitle;
+ private int mNotificationHeaderExpandedPadding;
+ private View mConversationHeader;
+ private View mContentContainer;
+ private boolean mExpandable = true;
+ private int mContentMarginEnd;
+
+ public ConversationLayout(@NonNull Context context) {
+ super(context);
+ }
+
+ public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mMessagingLinearLayout = findViewById(R.id.notification_messaging);
+ mMessagingLinearLayout.setMessagingLayout(this);
+ // We still want to clip, but only on the top, since views can temporarily out of bounds
+ // during transitions.
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
+ Rect rect = new Rect(0, 0, size, size);
+ mMessagingLinearLayout.setClipBounds(rect);
+ mTitleView = findViewById(R.id.title);
+ mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
+ mTextPaint.setTextAlign(Paint.Align.CENTER);
+ mTextPaint.setAntiAlias(true);
+ mConversationIcon = findViewById(R.id.conversation_icon);
+ mIcon = findViewById(R.id.icon);
+ mConversationIconBadge = findViewById(R.id.conversation_icon_badge);
+ mIcon.setOnVisibilityChangedListener((visibility) -> {
+ // Always keep the badge visibility in sync with the icon. This is necessary in cases
+ // Where the icon is being hidden externally like in group children.
+ mConversationIconBadge.setVisibility(visibility);
+ });
+ mConversationText = findViewById(R.id.conversation_text);
+ mExpandButtonContainer = findViewById(R.id.expand_button_container);
+ mConversationHeader = findViewById(R.id.conversation_header);
+ mContentContainer = findViewById(R.id.notification_action_list_margin_target);
+ mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
+ mExpandButton = findViewById(R.id.expand_button);
+ mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize(
+ R.dimen.conversation_expand_button_top_margin_expanded);
+ mExpandButtonExpandedSize = getResources().getDimensionPixelSize(
+ R.dimen.conversation_expand_button_expanded_size);
+ mNotificationHeaderExpandedPadding = getResources().getDimensionPixelSize(
+ R.dimen.conversation_header_expanded_padding_end);
+ mContentMarginEnd = getResources().getDimensionPixelSize(
+ R.dimen.notification_content_margin_end);
+ mBadgedSideMargins = getResources().getDimensionPixelSize(
+ R.dimen.conversation_badge_side_margin);
+ mIconSizeBadged = getResources().getDimensionPixelSize(
+ R.dimen.conversation_icon_size_badged);
+ mIconSizeCentered = getResources().getDimensionPixelSize(
+ R.dimen.conversation_icon_size_centered);
+ mExpandedGroupTopMargin = getResources().getDimensionPixelSize(
+ R.dimen.conversation_icon_margin_top_centered);
+ mConversationFacePile = findViewById(R.id.conversation_face_pile);
+ mFallbackChatName = getResources().getString(
+ R.string.conversation_title_fallback_one_to_one);
+ mFallbackGroupChatName = getResources().getString(
+ R.string.conversation_title_fallback_group_chat);
+ }
+
+ @RemotableViewMethod
+ public void setAvatarReplacement(Icon icon) {
+ mAvatarReplacement = icon;
+ }
+
+ @RemotableViewMethod
+ public void setNameReplacement(CharSequence nameReplacement) {
+ mNameReplacement = nameReplacement;
+ }
+
+ /**
+ * Set this layout to show the collapsed representation.
+ *
+ * @param isCollapsed is it collapsed
+ */
+ @RemotableViewMethod
+ public void setIsCollapsed(boolean isCollapsed) {
+ mIsCollapsed = isCollapsed;
+ mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE);
+ updateExpandButton();
+ updateContentPaddings();
+ }
+
+ @RemotableViewMethod
+ public void setData(Bundle extras) {
+ Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+ List<Notification.MessagingStyle.Message> newMessages
+ = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+ Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
+ List<Notification.MessagingStyle.Message> newHistoricMessages
+ = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
+
+ // mUser now set (would be nice to avoid the side effect but WHATEVER)
+ setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON));
+
+
+ // Append remote input history to newMessages (again, side effect is lame but WHATEVS)
+ RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[])
+ extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ addRemoteInputHistoryToMessages(newMessages, history);
+
+ boolean showSpinner =
+ extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
+
+ // bind it, baby
+ bind(newMessages, newHistoricMessages, showSpinner);
+ }
+
+ @Override
+ public void setImageResolver(ImageResolver resolver) {
+ mImageResolver = resolver;
+ }
+
+ private void addRemoteInputHistoryToMessages(
+ List<Notification.MessagingStyle.Message> newMessages,
+ RemoteInputHistoryItem[] remoteInputHistory) {
+ if (remoteInputHistory == null || remoteInputHistory.length == 0) {
+ return;
+ }
+ for (int i = remoteInputHistory.length - 1; i >= 0; i--) {
+ RemoteInputHistoryItem historyMessage = remoteInputHistory[i];
+ Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message(
+ historyMessage.getText(), 0, (Person) null, true /* remoteHistory */);
+ if (historyMessage.getUri() != null) {
+ message.setData(historyMessage.getMimeType(), historyMessage.getUri());
+ }
+ newMessages.add(message);
+ }
+ }
+
+ private void bind(List<Notification.MessagingStyle.Message> newMessages,
+ List<Notification.MessagingStyle.Message> newHistoricMessages,
+ boolean showSpinner) {
+ // convert MessagingStyle.Message to MessagingMessage, re-using ones from a previous binding
+ // if they exist
+ List<MessagingMessage> historicMessages = createMessages(newHistoricMessages,
+ true /* isHistoric */);
+ List<MessagingMessage> messages = createMessages(newMessages, false /* isHistoric */);
+
+ // Copy our groups, before they get clobbered
+ ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
+
+ // Add our new MessagingMessages to groups
+ List<List<MessagingMessage>> groups = new ArrayList<>();
+ List<Person> senders = new ArrayList<>();
+
+ // Lets first find the groups (populate `groups` and `senders`)
+ findGroups(historicMessages, messages, groups, senders);
+
+ // Let's now create the views and reorder them accordingly
+ // side-effect: updates mGroups, mAddedGroups
+ createGroupViews(groups, senders, showSpinner);
+
+ // Let's first check which groups were removed altogether and remove them in one animation
+ removeGroups(oldGroups);
+
+ // Let's remove the remaining messages
+ mMessages.forEach(REMOVE_MESSAGE);
+ mHistoricMessages.forEach(REMOVE_MESSAGE);
+
+ mMessages = messages;
+ mHistoricMessages = historicMessages;
+
+ updateHistoricMessageVisibility();
+ updateTitleAndNamesDisplay();
+
+ updateConversationLayout();
+
+ }
+
+ /**
+ * Update the layout according to the data provided (i.e mIsOneToOne, expanded etc);
+ */
+ private void updateConversationLayout() {
+ // TODO: resolve this from shortcuts
+ // Set avatar and name
+ CharSequence conversationText = mConversationTitle;
+ // TODO: display the secondary text somewhere
+ if (mIsOneToOne) {
+ // Let's resolve the icon / text from the last sender
+ mConversationIcon.setVisibility(VISIBLE);
+ mConversationFacePile.setVisibility(GONE);
+ CharSequence userKey = getKey(mUser);
+ for (int i = mGroups.size() - 1; i >= 0; i--) {
+ MessagingGroup messagingGroup = mGroups.get(i);
+ Person messageSender = messagingGroup.getSender();
+ if ((messageSender != null && !TextUtils.equals(userKey, getKey(messageSender)))
+ || i == 0) {
+ if (TextUtils.isEmpty(conversationText)) {
+ // We use the sendername as header text if no conversation title is provided
+ // (This usually happens for most 1:1 conversations)
+ conversationText = messagingGroup.getSenderName();
+ }
+ Icon avatarIcon = messagingGroup.getAvatarIcon();
+ if (avatarIcon == null) {
+ avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+ }
+ mConversationIcon.setImageIcon(avatarIcon);
+ break;
+ }
+ }
+ } else {
+ if (mIsCollapsed) {
+ if (mLargeIcon != null) {
+ mConversationIcon.setVisibility(VISIBLE);
+ mConversationFacePile.setVisibility(GONE);
+ mConversationIcon.setImageIcon(mLargeIcon);
+ } else {
+ mConversationIcon.setVisibility(GONE);
+ // This will also inflate it!
+ mConversationFacePile.setVisibility(VISIBLE);
+ mConversationFacePile = findViewById(R.id.conversation_face_pile);
+ bindFacePile();
+ }
+ } else {
+ mConversationFacePile.setVisibility(GONE);
+ mConversationIcon.setVisibility(GONE);
+ }
+ }
+ if (TextUtils.isEmpty(conversationText)) {
+ conversationText = mIsOneToOne ? mFallbackChatName : mFallbackGroupChatName;
+ }
+ mConversationText.setText(conversationText);
+ // Update if the groups can hide the sender if they are first (applies to 1:1 conversations)
+ // This needs to happen after all of the above o update all of the groups
+ for (int i = mGroups.size() - 1; i >= 0; i--) {
+ MessagingGroup messagingGroup = mGroups.get(i);
+ CharSequence messageSender = messagingGroup.getSenderName();
+ boolean canHide = mIsOneToOne
+ && TextUtils.equals(conversationText, messageSender);
+ messagingGroup.setCanHideSenderIfFirst(canHide);
+ }
+ updateIconPositionAndSize();
+ }
+
+ private void bindFacePile() {
+ // Let's bind the face pile
+ View bottomBackground = mConversationFacePile.findViewById(
+ R.id.conversation_face_pile_bottom_background);
+ applyNotificationBackgroundColor(bottomBackground);
+ ImageView bottomView = mConversationFacePile.findViewById(
+ R.id.conversation_face_pile_bottom);
+ ImageView topView = mConversationFacePile.findViewById(
+ R.id.conversation_face_pile_top);
+ // Let's find the two last conversations:
+ Icon secondLastIcon = null;
+ CharSequence lastKey = null;
+ Icon lastIcon = null;
+ CharSequence userKey = getKey(mUser);
+ for (int i = mGroups.size() - 1; i >= 0; i--) {
+ MessagingGroup messagingGroup = mGroups.get(i);
+ Person messageSender = messagingGroup.getSender();
+ boolean notUser = messageSender != null
+ && !TextUtils.equals(userKey, getKey(messageSender));
+ boolean notIncluded = messageSender != null
+ && !TextUtils.equals(lastKey, getKey(messageSender));
+ if ((notUser && notIncluded)
+ || (i == 0 && lastKey == null)) {
+ if (lastIcon == null) {
+ lastIcon = messagingGroup.getAvatarIcon();
+ lastKey = getKey(messageSender);
+ } else {
+ secondLastIcon = messagingGroup.getAvatarIcon();
+ break;
+ }
+ }
+ }
+ if (lastIcon == null) {
+ lastIcon = createAvatarSymbol(" ", "", mLayoutColor);
+ }
+ bottomView.setImageIcon(lastIcon);
+ if (secondLastIcon == null) {
+ secondLastIcon = createAvatarSymbol("", "", mLayoutColor);
+ }
+ topView.setImageIcon(secondLastIcon);
+ }
+
+ /**
+ * update the icon position and sizing
+ */
+ private void updateIconPositionAndSize() {
+ int gravity;
+ int marginStart;
+ int marginTop;
+ int iconSize;
+ if (mIsOneToOne || mIsCollapsed) {
+ // Baded format
+ gravity = Gravity.LEFT;
+ marginStart = mBadgedSideMargins;
+ marginTop = mBadgedSideMargins;
+ iconSize = mIconSizeBadged;
+ } else {
+ gravity = Gravity.CENTER_HORIZONTAL;
+ marginStart = 0;
+ marginTop = mExpandedGroupTopMargin;
+ iconSize = mIconSizeCentered;
+ }
+ LayoutParams layoutParams =
+ (LayoutParams) mConversationIconBadge.getLayoutParams();
+ layoutParams.gravity = gravity;
+ layoutParams.topMargin = marginTop;
+ layoutParams.setMarginStart(marginStart);
+ mConversationIconBadge.setLayoutParams(layoutParams);
+ ViewGroup.LayoutParams iconParams = mIcon.getLayoutParams();
+ iconParams.width = iconSize;
+ iconParams.height = iconSize;
+ mIcon.setLayoutParams(iconParams);
+ }
+
+ @RemotableViewMethod
+ public void setLargeIcon(Icon largeIcon) {
+ mLargeIcon = largeIcon;
+ }
+
+ /**
+ * Sets the conversation title of this conversation.
+ *
+ * @param conversationTitle the conversation title
+ */
+ @RemotableViewMethod
+ public void setConversationTitle(CharSequence conversationTitle) {
+ mConversationTitle = conversationTitle;
+ }
+
+ private void removeGroups(ArrayList<MessagingGroup> oldGroups) {
+ int size = oldGroups.size();
+ for (int i = 0; i < size; i++) {
+ MessagingGroup group = oldGroups.get(i);
+ if (!mGroups.contains(group)) {
+ List<MessagingMessage> messages = group.getMessages();
+ Runnable endRunnable = () -> {
+ mMessagingLinearLayout.removeTransientView(group);
+ group.recycle();
+ };
+
+ boolean wasShown = group.isShown();
+ mMessagingLinearLayout.removeView(group);
+ if (wasShown && !MessagingLinearLayout.isGone(group)) {
+ mMessagingLinearLayout.addTransientView(group, 0);
+ group.removeGroupAnimated(endRunnable);
+ } else {
+ endRunnable.run();
+ }
+ mMessages.removeAll(messages);
+ mHistoricMessages.removeAll(messages);
+ }
+ }
+ }
+
+ private void updateTitleAndNamesDisplay() {
+ ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>();
+ ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>();
+ for (int i = 0; i < mGroups.size(); i++) {
+ MessagingGroup group = mGroups.get(i);
+ CharSequence senderName = group.getSenderName();
+ if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) {
+ continue;
+ }
+ if (!uniqueNames.containsKey(senderName)) {
+ // Only use visible characters to get uniqueNames
+ String pureSenderName = IGNORABLE_CHAR_PATTERN
+ .matcher(senderName).replaceAll("" /* replacement */);
+ char c = pureSenderName.charAt(0);
+ if (uniqueCharacters.containsKey(c)) {
+ // this character was already used, lets make it more unique. We first need to
+ // resolve the existing character if it exists
+ CharSequence existingName = uniqueCharacters.get(c);
+ if (existingName != null) {
+ uniqueNames.put(existingName, findNameSplit((String) existingName));
+ uniqueCharacters.put(c, null);
+ }
+ uniqueNames.put(senderName, findNameSplit((String) senderName));
+ } else {
+ uniqueNames.put(senderName, Character.toString(c));
+ uniqueCharacters.put(c, pureSenderName);
+ }
+ }
+ }
+
+ // Now that we have the correct symbols, let's look what we have cached
+ ArrayMap<CharSequence, Icon> cachedAvatars = new ArrayMap<>();
+ for (int i = 0; i < mGroups.size(); i++) {
+ // Let's now set the avatars
+ MessagingGroup group = mGroups.get(i);
+ boolean isOwnMessage = group.getSender() == mUser;
+ CharSequence senderName = group.getSenderName();
+ if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)
+ || (mIsOneToOne && mAvatarReplacement != null && !isOwnMessage)) {
+ continue;
+ }
+ String symbol = uniqueNames.get(senderName);
+ Icon cachedIcon = group.getAvatarSymbolIfMatching(senderName,
+ symbol, mLayoutColor);
+ if (cachedIcon != null) {
+ cachedAvatars.put(senderName, cachedIcon);
+ }
+ }
+
+ for (int i = 0; i < mGroups.size(); i++) {
+ // Let's now set the avatars
+ MessagingGroup group = mGroups.get(i);
+ CharSequence senderName = group.getSenderName();
+ if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) {
+ continue;
+ }
+ if (mIsOneToOne && mAvatarReplacement != null && group.getSender() != mUser) {
+ group.setAvatar(mAvatarReplacement);
+ } else {
+ Icon cachedIcon = cachedAvatars.get(senderName);
+ if (cachedIcon == null) {
+ cachedIcon = createAvatarSymbol(senderName, uniqueNames.get(senderName),
+ mLayoutColor);
+ cachedAvatars.put(senderName, cachedIcon);
+ }
+ group.setCreatedAvatar(cachedIcon, senderName, uniqueNames.get(senderName),
+ mLayoutColor);
+ }
+ }
+ }
+
+ private Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) {
+ if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) ||
+ SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
+ Icon avatarIcon = Icon.createWithResource(getContext(),
+ R.drawable.messaging_user);
+ avatarIcon.setTint(findColor(senderName, layoutColor));
+ return avatarIcon;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ float radius = mAvatarSize / 2.0f;
+ int color = findColor(senderName, layoutColor);
+ mPaint.setColor(color);
+ canvas.drawCircle(radius, radius, radius, mPaint);
+ boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
+ mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
+ mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
+ int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
+ canvas.drawText(symbol, radius, yPos, mTextPaint);
+ return Icon.createWithBitmap(bitmap);
+ }
+ }
+
+ private int findColor(CharSequence senderName, int layoutColor) {
+ double luminance = ContrastColorUtil.calculateLuminance(layoutColor);
+ float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f;
+
+ // we need to offset the range if the luminance is too close to the borders
+ shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0);
+ shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0);
+ return ContrastColorUtil.getShiftedColor(layoutColor,
+ (int) (shift * COLOR_SHIFT_AMOUNT));
+ }
+
+ private String findNameSplit(String existingName) {
+ String[] split = existingName.split(" ");
+ if (split.length > 1) {
+ return Character.toString(split[0].charAt(0))
+ + Character.toString(split[1].charAt(0));
+ }
+ return existingName.substring(0, 1);
+ }
+
+ @RemotableViewMethod
+ public void setLayoutColor(int color) {
+ mLayoutColor = color;
+ }
+
+ @RemotableViewMethod
+ public void setIsOneToOne(boolean oneToOne) {
+ mIsOneToOne = oneToOne;
+ }
+
+ @RemotableViewMethod
+ public void setSenderTextColor(int color) {
+ mSenderTextColor = color;
+ }
+
+ /**
+ * @param color the color of the notification background
+ */
+ @RemotableViewMethod
+ public void setNotificationBackgroundColor(int color) {
+ mNotificationBackgroundColor = color;
+ applyNotificationBackgroundColor(mConversationIconBadge);
+ }
+
+ private void applyNotificationBackgroundColor(View view) {
+ view.setBackgroundTintList(ColorStateList.valueOf(mNotificationBackgroundColor));
+ }
+
+ @RemotableViewMethod
+ public void setMessageTextColor(int color) {
+ mMessageTextColor = color;
+ }
+
+ private void setUser(Person user) {
+ mUser = user;
+ if (mUser.getIcon() == null) {
+ Icon userIcon = Icon.createWithResource(getContext(),
+ R.drawable.messaging_user);
+ userIcon.setTint(mLayoutColor);
+ mUser = mUser.toBuilder().setIcon(userIcon).build();
+ }
+ }
+
+ private void createGroupViews(List<List<MessagingMessage>> groups,
+ List<Person> senders, boolean showSpinner) {
+ mGroups.clear();
+ for (int groupIndex = 0; groupIndex < groups.size(); groupIndex++) {
+ List<MessagingMessage> group = groups.get(groupIndex);
+ MessagingGroup newGroup = null;
+ // we'll just take the first group that exists or create one there is none
+ for (int messageIndex = group.size() - 1; messageIndex >= 0; messageIndex--) {
+ MessagingMessage message = group.get(messageIndex);
+ newGroup = message.getGroup();
+ if (newGroup != null) {
+ break;
+ }
+ }
+ // Create a new group, adding it to the linear layout as well
+ if (newGroup == null) {
+ newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
+ mAddedGroups.add(newGroup);
+ }
+ newGroup.setDisplayImagesAtEnd(mIsCollapsed);
+ newGroup.setIsInConversation(true);
+ newGroup.setLayoutColor(mLayoutColor);
+ newGroup.setTextColors(mSenderTextColor, mMessageTextColor);
+ Person sender = senders.get(groupIndex);
+ CharSequence nameOverride = null;
+ if (sender != mUser && mNameReplacement != null) {
+ nameOverride = mNameReplacement;
+ }
+ newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed);
+ newGroup.setSingleLine(mIsCollapsed);
+ newGroup.setSender(sender, nameOverride);
+ newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner);
+ mGroups.add(newGroup);
+
+ // Reposition to the correct place (if we're re-using a group)
+ if (mMessagingLinearLayout.indexOfChild(newGroup) != groupIndex) {
+ mMessagingLinearLayout.removeView(newGroup);
+ mMessagingLinearLayout.addView(newGroup, groupIndex);
+ }
+ newGroup.setMessages(group);
+ }
+ }
+
+ private void findGroups(List<MessagingMessage> historicMessages,
+ List<MessagingMessage> messages, List<List<MessagingMessage>> groups,
+ List<Person> senders) {
+ CharSequence currentSenderKey = null;
+ List<MessagingMessage> currentGroup = null;
+ int histSize = historicMessages.size();
+ for (int i = 0; i < histSize + messages.size(); i++) {
+ MessagingMessage message;
+ if (i < histSize) {
+ message = historicMessages.get(i);
+ } else {
+ message = messages.get(i - histSize);
+ }
+ boolean isNewGroup = currentGroup == null;
+ Person sender = message.getMessage().getSenderPerson();
+ CharSequence key = getKey(sender);
+ isNewGroup |= !TextUtils.equals(key, currentSenderKey);
+ if (isNewGroup) {
+ currentGroup = new ArrayList<>();
+ groups.add(currentGroup);
+ if (sender == null) {
+ sender = mUser;
+ }
+ senders.add(sender);
+ currentSenderKey = key;
+ }
+ currentGroup.add(message);
+ }
+ }
+
+ private CharSequence getKey(Person person) {
+ return person == null ? null : person.getKey() == null ? person.getName() : person.getKey();
+ }
+
+ /**
+ * Creates new messages, reusing existing ones if they are available.
+ *
+ * @param newMessages the messages to parse.
+ */
+ private List<MessagingMessage> createMessages(
+ List<Notification.MessagingStyle.Message> newMessages, boolean historic) {
+ List<MessagingMessage> result = new ArrayList<>();
+ for (int i = 0; i < newMessages.size(); i++) {
+ Notification.MessagingStyle.Message m = newMessages.get(i);
+ MessagingMessage message = findAndRemoveMatchingMessage(m);
+ if (message == null) {
+ message = MessagingMessage.createMessage(this, m, mImageResolver);
+ }
+ message.setIsHistoric(historic);
+ result.add(message);
+ }
+ return result;
+ }
+
+ private MessagingMessage findAndRemoveMatchingMessage(Notification.MessagingStyle.Message m) {
+ for (int i = 0; i < mMessages.size(); i++) {
+ MessagingMessage existing = mMessages.get(i);
+ if (existing.sameAs(m)) {
+ mMessages.remove(i);
+ return existing;
+ }
+ }
+ for (int i = 0; i < mHistoricMessages.size(); i++) {
+ MessagingMessage existing = mHistoricMessages.get(i);
+ if (existing.sameAs(m)) {
+ mHistoricMessages.remove(i);
+ return existing;
+ }
+ }
+ return null;
+ }
+
+ public void showHistoricMessages(boolean show) {
+ mShowHistoricMessages = show;
+ updateHistoricMessageVisibility();
+ }
+
+ private void updateHistoricMessageVisibility() {
+ int numHistoric = mHistoricMessages.size();
+ for (int i = 0; i < numHistoric; i++) {
+ MessagingMessage existing = mHistoricMessages.get(i);
+ existing.setVisibility(mShowHistoricMessages ? VISIBLE : GONE);
+ }
+ int numGroups = mGroups.size();
+ for (int i = 0; i < numGroups; i++) {
+ MessagingGroup group = mGroups.get(i);
+ int visibleChildren = 0;
+ List<MessagingMessage> messages = group.getMessages();
+ int numGroupMessages = messages.size();
+ for (int j = 0; j < numGroupMessages; j++) {
+ MessagingMessage message = messages.get(j);
+ if (message.getVisibility() != GONE) {
+ visibleChildren++;
+ }
+ }
+ if (visibleChildren > 0 && group.getVisibility() == GONE) {
+ group.setVisibility(VISIBLE);
+ } else if (visibleChildren == 0 && group.getVisibility() != GONE) {
+ group.setVisibility(GONE);
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (!mAddedGroups.isEmpty()) {
+ getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ for (MessagingGroup group : mAddedGroups) {
+ if (!group.isShown()) {
+ continue;
+ }
+ MessagingPropertyAnimator.fadeIn(group.getAvatar());
+ MessagingPropertyAnimator.fadeIn(group.getSenderView());
+ MessagingPropertyAnimator.startLocalTranslationFrom(group,
+ group.getHeight(), LINEAR_OUT_SLOW_IN);
+ }
+ mAddedGroups.clear();
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ return true;
+ }
+ });
+ }
+ }
+
+ public MessagingLinearLayout getMessagingLinearLayout() {
+ return mMessagingLinearLayout;
+ }
+
+ public ArrayList<MessagingGroup> getMessagingGroups() {
+ return mGroups;
+ }
+
+ private void updateExpandButton() {
+ int drawableId;
+ int contentDescriptionId;
+ int gravity;
+ int topMargin = 0;
+ ViewGroup newContainer;
+ int newContainerHeight;
+ if (mIsCollapsed) {
+ drawableId = R.drawable.ic_expand_notification;
+ contentDescriptionId = R.string.expand_button_content_description_collapsed;
+ gravity = Gravity.CENTER;
+ newContainer = mExpandButtonAndContentContainer;
+ newContainerHeight = LayoutParams.MATCH_PARENT;
+ } else {
+ drawableId = R.drawable.ic_collapse_notification;
+ contentDescriptionId = R.string.expand_button_content_description_expanded;
+ gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ topMargin = mExpandButtonExpandedTopMargin;
+ newContainer = this;
+ newContainerHeight = mExpandButtonExpandedSize;
+ }
+ mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
+ mExpandButton.setColorFilter(mExpandButton.getOriginalNotificationColor());
+
+ // We need to make sure that the expand button is in the linearlayout pushing over the
+ // content when collapsed, but allows the content to flow under it when expanded.
+ if (newContainer != mExpandButtonContainer.getParent()) {
+ ((ViewGroup) mExpandButtonContainer.getParent()).removeView(mExpandButtonContainer);
+ newContainer.addView(mExpandButtonContainer);
+ MarginLayoutParams layoutParams =
+ (MarginLayoutParams) mExpandButtonContainer.getLayoutParams();
+ layoutParams.height = newContainerHeight;
+ mExpandButtonContainer.setLayoutParams(layoutParams);
+ }
+
+ // update if the expand button is centered
+ FrameLayout.LayoutParams layoutParams = (LayoutParams) mExpandButton.getLayoutParams();
+ layoutParams.gravity = gravity;
+ layoutParams.topMargin = topMargin;
+ mExpandButton.setLayoutParams(layoutParams);
+
+ mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId));
+ }
+
+ private void updateContentPaddings() {
+
+ // Let's make sure the conversation header can't run into the expand button when we're
+ // collapsed and update the paddings of the content
+ int headerPaddingEnd;
+ int contentPaddingEnd;
+ if (!mExpandable) {
+ headerPaddingEnd = 0;
+ contentPaddingEnd = mContentMarginEnd;
+ } else if (mIsCollapsed) {
+ headerPaddingEnd = 0;
+ contentPaddingEnd = 0;
+ } else {
+ headerPaddingEnd = mNotificationHeaderExpandedPadding;
+ contentPaddingEnd = mContentMarginEnd;
+ }
+ mConversationHeader.setPaddingRelative(
+ mConversationHeader.getPaddingStart(),
+ mConversationHeader.getPaddingTop(),
+ headerPaddingEnd,
+ mConversationHeader.getPaddingBottom());
+
+ mContentContainer.setPaddingRelative(
+ mContentContainer.getPaddingStart(),
+ mContentContainer.getPaddingTop(),
+ contentPaddingEnd,
+ mContentContainer.getPaddingBottom());
+ }
+
+ public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) {
+ mExpandable = expandable;
+ if (expandable) {
+ mExpandButtonContainer.setVisibility(VISIBLE);
+ mExpandButtonContainer.setOnClickListener(onClickListener);
+ } else {
+ // TODO: handle content paddings to end of layout
+ mExpandButtonContainer.setVisibility(GONE);
+ }
+ updateContentPaddings();
+ }
+}
diff --git a/core/java/com/android/internal/widget/IMessagingLayout.java b/core/java/com/android/internal/widget/IMessagingLayout.java
new file mode 100644
index 0000000..149d056
--- /dev/null
+++ b/core/java/com/android/internal/widget/IMessagingLayout.java
@@ -0,0 +1,42 @@
+/*
+ * 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.internal.widget;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+
+/**
+ * An interface for a MessagingLayout
+ */
+public interface IMessagingLayout {
+
+ /**
+ * @return the layout containing the messages
+ */
+ MessagingLinearLayout getMessagingLinearLayout();
+
+ /**
+ * @return the context of this view
+ */
+ Context getContext();
+
+ /**
+ * @return the list of messaging groups
+ */
+ ArrayList<MessagingGroup> getMessagingGroups();
+}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index c9a9161..c68da97 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -55,8 +55,9 @@
private static Pools.SimplePool<MessagingGroup> sInstancePool
= new Pools.SynchronizedPool<>(10);
private MessagingLinearLayout mMessageContainer;
- private ImageFloatingTextView mSenderName;
+ ImageFloatingTextView mSenderView;
private ImageView mAvatarView;
+ private View mAvatarContainer;
private String mAvatarSymbol = "";
private int mLayoutColor;
private CharSequence mAvatarName = "";
@@ -72,10 +73,22 @@
private boolean mImagesAtEnd;
private ViewGroup mImageContainer;
private MessagingImageMessage mIsolatedMessage;
- private boolean mTransformingImages;
+ private boolean mClippingDisabled;
private Point mDisplaySize = new Point();
private ProgressBar mSendingSpinner;
private View mSendingSpinnerContainer;
+ private boolean mShowingAvatar = true;
+ private CharSequence mSenderName;
+ private boolean mSingleLine = false;
+ private LinearLayout mContentContainer;
+ private int mRequestedMaxDisplayedLines = Integer.MAX_VALUE;
+ private int mSenderTextPaddingSingleLine;
+ private boolean mIsFirstGroupInLayout = true;
+ private boolean mCanHideSenderIfFirst;
+ private boolean mIsInConversation = true;
+ private ViewGroup mMessagingIconContainer;
+ private int mConversationContentStart;
+ private int mNonConversationMarginEnd;
public MessagingGroup(@NonNull Context context) {
super(context);
@@ -99,26 +112,39 @@
protected void onFinishInflate() {
super.onFinishInflate();
mMessageContainer = findViewById(R.id.group_message_container);
- mSenderName = findViewById(R.id.message_name);
+ mSenderView = findViewById(R.id.message_name);
mAvatarView = findViewById(R.id.message_icon);
mImageContainer = findViewById(R.id.messaging_group_icon_container);
mSendingSpinner = findViewById(R.id.messaging_group_sending_progress);
+ mMessagingIconContainer = findViewById(R.id.message_icon_container);
+ mContentContainer = findViewById(R.id.messaging_group_content_container);
mSendingSpinnerContainer = findViewById(R.id.messaging_group_sending_progress_container);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
mDisplaySize.x = displayMetrics.widthPixels;
mDisplaySize.y = displayMetrics.heightPixels;
+ mSenderTextPaddingSingleLine = getResources().getDimensionPixelSize(
+ R.dimen.messaging_group_singleline_sender_padding_end);
+ mConversationContentStart = getResources().getDimensionPixelSize(
+ R.dimen.conversation_content_start);
+ mNonConversationMarginEnd = getResources().getDimensionPixelSize(
+ R.dimen.messaging_layout_margin_end);
}
public void updateClipRect() {
// We want to clip to the senderName if it's available, otherwise our images will come
// from a weird position
Rect clipRect;
- if (mSenderName.getVisibility() != View.GONE && !mTransformingImages) {
- ViewGroup parent = (ViewGroup) mSenderName.getParent();
- int top = getDistanceFromParent(mSenderName, parent) - getDistanceFromParent(
- mMessageContainer, parent) + mSenderName.getHeight();
+ if (mSenderView.getVisibility() != View.GONE && !mClippingDisabled) {
+ int top;
+ if (mSingleLine) {
+ top = 0;
+ } else {
+ top = getDistanceFromParent(mSenderView, mContentContainer)
+ - getDistanceFromParent(mMessageContainer, mContentContainer)
+ + mSenderView.getHeight();
+ }
int size = Math.max(mDisplaySize.x, mDisplaySize.y);
- clipRect = new Rect(0, top, size, size);
+ clipRect = new Rect(-size, top, size, size);
} else {
clipRect = null;
}
@@ -140,17 +166,31 @@
if (nameOverride == null) {
nameOverride = sender.getName();
}
- mSenderName.setText(nameOverride);
+ mSenderName = nameOverride;
+ if (mSingleLine && !TextUtils.isEmpty(nameOverride)) {
+ nameOverride = mContext.getResources().getString(
+ R.string.conversation_single_line_name_display, nameOverride);
+ }
+ mSenderView.setText(nameOverride);
mNeedsGeneratedAvatar = sender.getIcon() == null;
if (!mNeedsGeneratedAvatar) {
setAvatar(sender.getIcon());
}
- mAvatarView.setVisibility(VISIBLE);
- mSenderName.setVisibility(TextUtils.isEmpty(nameOverride) ? GONE : VISIBLE);
+ updateSenderVisibility();
+ }
+
+ /**
+ * Should the avatar be shown for this view.
+ *
+ * @param showingAvatar should it be shown
+ */
+ public void setShowingAvatar(boolean showingAvatar) {
+ mAvatarView.setVisibility(showingAvatar ? VISIBLE : GONE);
+ mShowingAvatar = showingAvatar;
}
public void setSending(boolean sending) {
- int visibility = sending ? View.VISIBLE : View.GONE;
+ int visibility = sending ? VISIBLE : GONE;
if (mSendingSpinnerContainer.getVisibility() != visibility) {
mSendingSpinnerContainer.setVisibility(visibility);
updateMessageColor();
@@ -171,7 +211,9 @@
public void setAvatar(Icon icon) {
mAvatarIcon = icon;
- mAvatarView.setImageIcon(icon);
+ if (mShowingAvatar || icon == null) {
+ mAvatarView.setImageIcon(icon);
+ }
mAvatarSymbol = "";
mAvatarName = "";
}
@@ -220,13 +262,20 @@
setAvatar(null);
mAvatarView.setAlpha(1.0f);
mAvatarView.setTranslationY(0.0f);
- mSenderName.setAlpha(1.0f);
- mSenderName.setTranslationY(0.0f);
+ mSenderView.setAlpha(1.0f);
+ mSenderView.setTranslationY(0.0f);
setAlpha(1.0f);
mIsolatedMessage = null;
mMessages = null;
+ mSenderName = null;
mAddedMessages.clear();
mFirstLayout = true;
+ setCanHideSenderIfFirst(false);
+ setIsFirstInLayout(true);
+
+ setMaxDisplayedLines(Integer.MAX_VALUE);
+ setSingleLine(false);
+ setShowingAvatar(true);
MessagingPropertyAnimator.recycle(this);
sInstancePool.release(MessagingGroup.this);
}
@@ -252,7 +301,7 @@
}
public CharSequence getSenderName() {
- return mSenderName.getText();
+ return mSenderName;
}
public static void dropCache() {
@@ -310,7 +359,12 @@
@Override
public void setMaxDisplayedLines(int lines) {
- mMessageContainer.setMaxDisplayedLines(lines);
+ mRequestedMaxDisplayedLines = lines;
+ updateMaxDisplayedLines();
+ }
+
+ private void updateMaxDisplayedLines() {
+ mMessageContainer.setMaxDisplayedLines(mSingleLine ? 1 : mRequestedMaxDisplayedLines);
}
@Override
@@ -324,6 +378,35 @@
return mIsHidingAnimated;
}
+ @Override
+ public void setIsFirstInLayout(boolean first) {
+ if (first != mIsFirstGroupInLayout) {
+ mIsFirstGroupInLayout = first;
+ updateSenderVisibility();
+ }
+ }
+
+ /**
+ * @param canHide true if the sender can be hidden if it is first
+ */
+ public void setCanHideSenderIfFirst(boolean canHide) {
+ if (mCanHideSenderIfFirst != canHide) {
+ mCanHideSenderIfFirst = canHide;
+ updateSenderVisibility();
+ }
+ }
+
+ private void updateSenderVisibility() {
+ boolean hidden = (mIsFirstGroupInLayout || mSingleLine) && mCanHideSenderIfFirst
+ || TextUtils.isEmpty(mSenderName);
+ mSenderView.setVisibility(hidden ? GONE : VISIBLE);
+ }
+
+ @Override
+ public boolean hasDifferentHeightWhenFirst() {
+ return mCanHideSenderIfFirst && !mSingleLine && !TextUtils.isEmpty(mSenderName);
+ }
+
private void setIsHidingAnimated(boolean isHiding) {
ViewParent parent = getParent();
mIsHidingAnimated = isHiding;
@@ -362,7 +445,7 @@
mTextColor = messageTextColor;
mSendingTextColor = calculateSendingTextColor();
updateMessageColor();
- mSenderName.setTextColor(senderTextColor);
+ mSenderView.setTextColor(senderTextColor);
}
public void setLayoutColor(int layoutColor) {
@@ -506,13 +589,17 @@
}
public View getSenderView() {
- return mSenderName;
+ return mSenderView;
}
public View getAvatar() {
return mAvatarView;
}
+ public Icon getAvatarIcon() {
+ return mAvatarIcon;
+ }
+
public MessagingLinearLayout getMessageContainer() {
return mMessageContainer;
}
@@ -529,8 +616,8 @@
return mSender;
}
- public void setTransformingImages(boolean transformingImages) {
- mTransformingImages = transformingImages;
+ public void setClippingDisabled(boolean disabled) {
+ mClippingDisabled = disabled;
}
public void setDisplayImagesAtEnd(boolean atEnd) {
@@ -543,4 +630,44 @@
public List<MessagingMessage> getMessages() {
return mMessages;
}
+
+ /**
+ * Set this layout to be single line and therefore displaying both the sender and the text on
+ * the same line.
+ *
+ * @param singleLine should be layout be single line
+ */
+ public void setSingleLine(boolean singleLine) {
+ if (singleLine != mSingleLine) {
+ mSingleLine = singleLine;
+ mContentContainer.setOrientation(
+ singleLine ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mSenderView.getLayoutParams();
+ layoutParams.setMarginEnd(singleLine ? mSenderTextPaddingSingleLine : 0);
+ updateMaxDisplayedLines();
+ updateClipRect();
+ updateSenderVisibility();
+ }
+ }
+
+ public boolean isSingleLine() {
+ return mSingleLine;
+ }
+
+ /**
+ * Set this group to be displayed in a conversation and adjust the visual appearance
+ *
+ * @param isInConversation is this in a conversation
+ */
+ public void setIsInConversation(boolean isInConversation) {
+ if (mIsInConversation != isInConversation) {
+ mIsInConversation = isInConversation;
+ MarginLayoutParams layoutParams =
+ (MarginLayoutParams) mMessagingIconContainer.getLayoutParams();
+ layoutParams.width = mIsInConversation ? mConversationContentStart
+ : ViewPager.LayoutParams.WRAP_CONTENT;
+ layoutParams.setMarginEnd(mIsInConversation ? 0 : mNonConversationMarginEnd);
+ mMessagingIconContainer.setLayoutParams(layoutParams);
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
index 64650a7..c243f3b 100644
--- a/core/java/com/android/internal/widget/MessagingImageMessage.java
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -120,7 +120,7 @@
return true;
}
- static MessagingMessage createMessage(MessagingLayout layout,
+ static MessagingMessage createMessage(IMessagingLayout layout,
Notification.MessagingStyle.Message m, ImageResolver resolver) {
MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
MessagingImageMessage createdMessage = sInstancePool.acquire();
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index f608958..3fb5d43 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -58,7 +58,8 @@
* messages and adapts the layout accordingly.
*/
@RemoteViews.RemoteView
-public class MessagingLayout extends FrameLayout implements ImageMessageConsumer {
+public class MessagingLayout extends FrameLayout
+ implements ImageMessageConsumer, IMessagingLayout {
private static final float COLOR_SHIFT_AMOUNT = 60;
/**
@@ -143,9 +144,29 @@
mNameReplacement = nameReplacement;
}
+ /**
+ * Set this layout to show the collapsed representation.
+ *
+ * @param isCollapsed is it collapsed
+ */
@RemotableViewMethod
- public void setDisplayImagesAtEnd(boolean atEnd) {
- mDisplayImagesAtEnd = atEnd;
+ public void setIsCollapsed(boolean isCollapsed) {
+ mDisplayImagesAtEnd = isCollapsed;
+ }
+
+ @RemotableViewMethod
+ public void setLargeIcon(Icon largeIcon) {
+ // Unused
+ }
+
+ /**
+ * Sets the conversation title of this conversation.
+ *
+ * @param conversationTitle the conversation title
+ */
+ @RemotableViewMethod
+ public void setConversationTitle(CharSequence conversationTitle) {
+ // Unused
}
@RemotableViewMethod
@@ -371,6 +392,15 @@
mSenderTextColor = color;
}
+
+ /**
+ * @param color the color of the notification background
+ */
+ @RemotableViewMethod
+ public void setNotificationBackgroundColor(int color) {
+ // Nothing to do with this
+ }
+
@RemotableViewMethod
public void setMessageTextColor(int color) {
mMessageTextColor = color;
@@ -418,6 +448,7 @@
mAddedGroups.add(newGroup);
}
newGroup.setDisplayImagesAtEnd(mDisplayImagesAtEnd);
+ newGroup.setIsInConversation(false);
newGroup.setLayoutColor(mLayoutColor);
newGroup.setTextColors(mSenderTextColor, mMessageTextColor);
Person sender = senders.get(groupIndex);
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index 0c8613b..ac04862 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -43,7 +43,7 @@
private int mMaxDisplayedLines = Integer.MAX_VALUE;
- private MessagingLayout mMessagingLayout;
+ private IMessagingLayout mMessagingLayout;
public MessagingLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -84,6 +84,11 @@
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.hide = true;
+ if (child instanceof MessagingChild) {
+ MessagingChild messagingChild = (MessagingChild) child;
+ // Whenever we encounter the message first, it's always first in the layout
+ messagingChild.setIsFirstInLayout(true);
+ }
}
totalHeight = mPaddingTop + mPaddingBottom;
@@ -91,6 +96,11 @@
int linesRemaining = mMaxDisplayedLines;
// Starting from the bottom: we measure every view as if it were the only one. If it still
// fits, we take it, otherwise we stop there.
+ MessagingChild previousChild = null;
+ View previousView = null;
+ int previousChildHeight = 0;
+ int previousTotalHeight = 0;
+ int previousLinesConsumed = 0;
for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) {
if (getChildAt(i).getVisibility() == GONE) {
continue;
@@ -99,7 +109,16 @@
LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
MessagingChild messagingChild = null;
int spacing = mSpacing;
+ int previousChildIncrease = 0;
if (child instanceof MessagingChild) {
+ // We need to remeasure the previous child again if it's not the first anymore
+ if (previousChild != null && previousChild.hasDifferentHeightWhenFirst()) {
+ previousChild.setIsFirstInLayout(false);
+ measureChildWithMargins(previousView, widthMeasureSpec, 0, heightMeasureSpec,
+ previousTotalHeight - previousChildHeight);
+ previousChildIncrease = previousView.getMeasuredHeight() - previousChildHeight;
+ linesRemaining -= previousChild.getConsumedLines() - previousLinesConsumed;
+ }
messagingChild = (MessagingChild) child;
messagingChild.setMaxDisplayedLines(linesRemaining);
spacing += messagingChild.getExtraSpacing();
@@ -110,18 +129,26 @@
final int childHeight = child.getMeasuredHeight();
int newHeight = Math.max(totalHeight, totalHeight + childHeight + lp.topMargin +
- lp.bottomMargin + spacing);
+ lp.bottomMargin + spacing + previousChildIncrease);
int measureType = MessagingChild.MEASURED_NORMAL;
if (messagingChild != null) {
measureType = messagingChild.getMeasuredType();
- linesRemaining -= messagingChild.getConsumedLines();
}
// We never measure the first item as too small, we want to at least show something.
boolean isTooSmall = measureType == MessagingChild.MEASURED_TOO_SMALL && !first;
boolean isShortened = measureType == MessagingChild.MEASURED_SHORTENED
|| measureType == MessagingChild.MEASURED_TOO_SMALL && first;
- if (newHeight <= targetHeight && !isTooSmall) {
+ boolean showView = newHeight <= targetHeight && !isTooSmall;
+ if (showView) {
+ if (messagingChild != null) {
+ previousLinesConsumed = messagingChild.getConsumedLines();
+ linesRemaining -= previousLinesConsumed;
+ previousChild = messagingChild;
+ previousView = child;
+ previousChildHeight = childHeight;
+ previousTotalHeight = totalHeight;
+ }
totalHeight = newHeight;
measuredWidth = Math.max(measuredWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin
@@ -131,6 +158,16 @@
break;
}
} else {
+ // We now became too short, let's make sure to reset any previous views to be first
+ // and remeasure it.
+ if (previousChild != null && previousChild.hasDifferentHeightWhenFirst()) {
+ previousChild.setIsFirstInLayout(true);
+ // We need to remeasure the previous child again since it became first
+ measureChildWithMargins(previousView, widthMeasureSpec, 0, heightMeasureSpec,
+ previousTotalHeight - previousChildHeight);
+ // The totalHeight is already correct here since we only set it during the
+ // first pass
+ }
break;
}
first = false;
@@ -255,11 +292,11 @@
mMaxDisplayedLines = numberLines;
}
- public void setMessagingLayout(MessagingLayout layout) {
+ public void setMessagingLayout(IMessagingLayout layout) {
mMessagingLayout = layout;
}
- public MessagingLayout getMessagingLayout() {
+ public IMessagingLayout getMessagingLayout() {
return mMessagingLayout;
}
@@ -273,6 +310,20 @@
void setMaxDisplayedLines(int lines);
void hideAnimated();
boolean isHidingAnimated();
+
+ /**
+ * Set that this view is first in layout. Relevant and only set if
+ * {@link #hasDifferentHeightWhenFirst()}.
+ * @param first is this first?
+ */
+ default void setIsFirstInLayout(boolean first) {}
+
+ /**
+ * @return if this layout has different height it is first in the layout
+ */
+ default boolean hasDifferentHeightWhenFirst() {
+ return false;
+ }
default int getExtraSpacing() {
return 0;
}
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index c32d370..8c84379 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -32,7 +32,7 @@
**/
String IMAGE_MIME_TYPE_PREFIX = "image/";
- static MessagingMessage createMessage(MessagingLayout layout,
+ static MessagingMessage createMessage(IMessagingLayout layout,
Notification.MessagingStyle.Message m, ImageResolver resolver) {
if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) {
return MessagingImageMessage.createMessage(layout, m, resolver);
diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java
index 4081a86..d778c59 100644
--- a/core/java/com/android/internal/widget/MessagingTextMessage.java
+++ b/core/java/com/android/internal/widget/MessagingTextMessage.java
@@ -26,14 +26,10 @@
import android.util.AttributeSet;
import android.util.Pools;
import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.view.ViewParent;
import android.widget.RemoteViews;
import com.android.internal.R;
-import java.util.Objects;
-
/**
* A message of a {@link MessagingLayout}.
*/
@@ -74,7 +70,7 @@
return true;
}
- static MessagingMessage createMessage(MessagingLayout layout,
+ static MessagingMessage createMessage(IMessagingLayout layout,
Notification.MessagingStyle.Message m) {
MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
MessagingTextMessage createdMessage = sInstancePool.acquire();
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index 39f82a5..a499806 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -20,7 +20,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.view.View;
+import android.view.RemotableViewMethod;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Button;
import android.widget.ImageView;
@@ -32,6 +32,8 @@
@RemoteViews.RemoteView
public class NotificationExpandButton extends ImageView {
+ private int mOriginalNotificationColor;
+
public NotificationExpandButton(Context context) {
super(context);
}
@@ -56,6 +58,15 @@
extendRectToMinTouchSize(outRect);
}
+ @RemotableViewMethod
+ public void setOriginalNotificationColor(int color) {
+ mOriginalNotificationColor = color;
+ }
+
+ public int getOriginalNotificationColor() {
+ return mOriginalNotificationColor;
+ }
+
private void extendRectToMinTouchSize(Rect rect) {
int touchTargetSize = (int) (getResources().getDisplayMetrics().density * 48);
rect.left = rect.centerX() - touchTargetSize / 2;
diff --git a/core/java/com/android/internal/widget/RemeasuringLinearLayout.java b/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
index e352b45..7b154a5 100644
--- a/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
+++ b/core/java/com/android/internal/widget/RemeasuringLinearLayout.java
@@ -23,6 +23,8 @@
import android.widget.LinearLayout;
import android.widget.RemoteViews;
+import java.util.ArrayList;
+
/**
* A LinearLayout that sets it's height again after the last measure pass. This is needed for
* MessagingLayouts where groups need to be able to snap it's height to.
@@ -30,6 +32,8 @@
@RemoteViews.RemoteView
public class RemeasuringLinearLayout extends LinearLayout {
+ private ArrayList<View> mMatchParentViews = new ArrayList<>();
+
public RemeasuringLinearLayout(Context context) {
super(context);
}
@@ -53,6 +57,8 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
int height = 0;
+ boolean isVertical = getOrientation() == LinearLayout.VERTICAL;
+ boolean isWrapContent = getLayoutParams().height == LayoutParams.WRAP_CONTENT;
for (int i = 0; i < count; ++i) {
final View child = getChildAt(i);
if (child == null || child.getVisibility() == View.GONE) {
@@ -60,9 +66,25 @@
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- height = Math.max(height, height + child.getMeasuredHeight() + lp.topMargin +
- lp.bottomMargin);
+ if (!isWrapContent || lp.height != LayoutParams.MATCH_PARENT || isVertical) {
+ int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
+ height = Math.max(height, isVertical ? height + childHeight : childHeight);
+ } else {
+ // We have match parent children in a wrap content view, let's measure the
+ // view properly
+ mMatchParentViews.add(child);
+ }
}
+ if (mMatchParentViews.size() > 0) {
+ int exactHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+ for (View child : mMatchParentViews) {
+ child.measure(getChildMeasureSpec(
+ widthMeasureSpec, getPaddingStart() + getPaddingEnd(),
+ child.getLayoutParams().width),
+ exactHeightSpec);
+ }
+ }
+ mMatchParentViews.clear();
setMeasuredDimension(getMeasuredWidth(), height);
}
}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 0d0dc3e..f7c6dbd 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -853,8 +853,11 @@
} cfg_state = CONFIG_UNKNOWN;
if (cfg_state == CONFIG_UNKNOWN) {
- const std::map<std::string, std::string> configs =
- vintf::VintfObject::GetInstance()->getRuntimeInfo()->kernelConfigs();
+ auto runtime_info = vintf::VintfObject::GetInstance()
+ ->getRuntimeInfo(false /* skip cache */,
+ vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
+ CHECK(runtime_info != nullptr) << "Kernel configs cannot be fetched. b/151092221";
+ const std::map<std::string, std::string>& configs = runtime_info->kernelConfigs();
std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK");
cfg_state = (it != configs.end() && it->second == "y") ? CONFIG_SET : CONFIG_UNSET;
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 0eb364d..b32b4ae 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -346,6 +346,22 @@
}
}
+void android_os_Process_enableFreezer(
+ JNIEnv *env, jobject clazz, jboolean enable)
+{
+ bool success = true;
+
+ if (enable) {
+ success = SetTaskProfiles(0, {"FreezerFrozen"}, true);
+ } else {
+ success = SetTaskProfiles(0, {"FreezerThawed"}, true);
+ }
+
+ if (!success) {
+ jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+ }
+}
+
jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid)
{
SchedPolicy sp;
@@ -1344,6 +1360,7 @@
{"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
{"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet},
{"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen},
+ {"enableFreezer", "(Z)V", (void*)android_os_Process_enableFreezer},
{"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
{"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory},
{"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 2128f99..ea3c0fa 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -121,16 +121,11 @@
static pid_t gSystemServerPid = 0;
static constexpr const char* kPropFuse = "persist.sys.fuse";
-static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote";
-
+static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
static jclass gZygoteClass;
static jmethodID gCallPostForkSystemServerHooks;
static jmethodID gCallPostForkChildHooks;
-static constexpr const char* kZygoteInitClassName = "com/android/internal/os/ZygoteInit";
-static jclass gZygoteInitClass;
-static jmethodID gCreateSystemServerClassLoader;
-
static bool gIsSecurityEnforced = true;
/**
@@ -356,10 +351,14 @@
// Must match values in com.android.internal.os.Zygote.
enum RuntimeFlags : uint32_t {
- DEBUG_ENABLE_JDWP = 1,
- PROFILE_FROM_SHELL = 1 << 15,
- MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
- MEMORY_TAG_LEVEL_TBI = 1 << 19,
+ DEBUG_ENABLE_JDWP = 1,
+ PROFILE_FROM_SHELL = 1 << 15,
+ MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
+ MEMORY_TAG_LEVEL_TBI = 1 << 19,
+ GWP_ASAN_LEVEL_MASK = (1 << 21) | (1 << 22),
+ GWP_ASAN_LEVEL_NEVER = 0 << 21,
+ GWP_ASAN_LEVEL_LOTTERY = 1 << 21,
+ GWP_ASAN_LEVEL_ALWAYS = 2 << 21,
};
enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1568,23 +1567,6 @@
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
const userid_t user_id = multiuser_get_user_id(uid);
- // If FUSE is not ready for this user, skip it
- // TODO(148772775): Pass primary volume name from zygote argument to here
- std::string tmp = GetProperty("vold.fuse_running_users", "");
- std::istringstream fuse_running_users(tmp);
- bool user_found = false;
- std::string s;
- std::string user_id_str = std::to_string(user_id);
- while (!user_found && std::getline(fuse_running_users, s, ',')) {
- if (user_id_str == s) {
- user_found = true;
- }
- }
- if (!user_found) {
- ALOGI("User %d is not running fuse yet, fuse_running_users=%s", user_id, tmp.c_str());
- return;
- }
-
// Fuse is ready, so we can start using fuse path.
int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
@@ -1612,7 +1594,7 @@
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,
jstring managed_app_data_dir, bool is_top_app,
- jobjectArray pkg_data_info_list) {
+ jobjectArray pkg_data_info_list, bool mount_storage_dirs) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1651,10 +1633,7 @@
isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
-
- if ((mount_external != MOUNT_EXTERNAL_INSTALLER) &&
- GetBoolProperty(kPropFuse, false) &&
- GetBoolProperty(ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+ if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) {
BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
@@ -1741,6 +1720,18 @@
}
android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
+ bool forceEnableGwpAsan = false;
+ switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
+ default:
+ case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
+ break;
+ case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
+ forceEnableGwpAsan = true;
+ [[fallthrough]];
+ case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
+ }
+
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
int old_personality = personality(0xffffffff);
@@ -1780,15 +1771,6 @@
fail_fn("Error calling post fork system server hooks.");
}
- // Prefetch the classloader for the system server. This is done early to
- // allow a tie-down of the proper system server selinux domain.
- env->CallStaticVoidMethod(gZygoteInitClass, gCreateSystemServerClassLoader);
- if (env->ExceptionCheck()) {
- // Be robust here. The Java code will attempt to create the classloader
- // at a later point (but may not have rights to use AoT artifacts).
- env->ExceptionClear();
- }
-
// TODO(oth): Remove hardcoded label here (b/117874058).
static const char* kSystemServerLabel = "u:r:system_server:s0";
if (selinux_android_setcon(kSystemServerLabel) != 0) {
@@ -2021,7 +2003,7 @@
jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list) {
+ jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -2058,7 +2040,8 @@
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- is_top_app == JNI_TRUE, pkg_data_info_list);
+ is_top_app == JNI_TRUE, pkg_data_info_list,
+ mount_storage_dirs == JNI_TRUE);
}
return pid;
}
@@ -2093,7 +2076,7 @@
permitted_capabilities, effective_capabilities,
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, /* is_top_app= */ false,
- /* pkg_data_info_list */ nullptr);
+ /* pkg_data_info_list */ nullptr, false);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -2223,14 +2206,15 @@
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list) {
+ jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- is_top_app == JNI_TRUE, pkg_data_info_list);
+ is_top_app == JNI_TRUE, pkg_data_info_list,
+ mount_storage_dirs == JNI_TRUE);
}
/**
@@ -2424,7 +2408,7 @@
static const JNINativeMethod gMethods[] = {
{"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;)I",
+ "String;Z[Ljava/lang/String;Z)I",
(void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
{"nativeForkSystemServer", "(II[II[[IJJ)I",
(void*)com_android_internal_os_Zygote_nativeForkSystemServer},
@@ -2437,7 +2421,7 @@
{"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
{"nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;)V",
+ "String;Z[Ljava/lang/String;Z)V",
(void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
{"nativeInitNativeState", "(Z)V",
(void*)com_android_internal_os_Zygote_nativeInitNativeState},
@@ -2466,13 +2450,6 @@
gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
"(IZZLjava/lang/String;)V");
- gZygoteInitClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteInitClassName));
- gCreateSystemServerClassLoader = GetStaticMethodIDOrDie(env, gZygoteInitClass,
- "createSystemServerClassLoader",
- "()V");
-
- RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
-
- return JNI_OK;
+ return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
}
} // namespace android
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
index 9abe923..42437d5 100644
--- a/core/proto/android/app/enums.proto
+++ b/core/proto/android/app/enums.proto
@@ -104,3 +104,108 @@
PROCESS_STATE_NONEXISTENT = 1019;
}
+// AppOpsManager.java - operation ids for logging
+enum AppOpEnum {
+ APP_OP_NONE = -1;
+ APP_OP_COARSE_LOCATION = 0;
+ APP_OP_FINE_LOCATION = 1;
+ APP_OP_GPS = 2;
+ APP_OP_VIBRATE = 3;
+ APP_OP_READ_CONTACTS = 4;
+ APP_OP_WRITE_CONTACTS = 5;
+ APP_OP_READ_CALL_LOG = 6;
+ APP_OP_WRITE_CALL_LOG = 7;
+ APP_OP_READ_CALENDAR = 8;
+ APP_OP_WRITE_CALENDAR = 9;
+ APP_OP_WIFI_SCAN = 10;
+ APP_OP_POST_NOTIFICATION = 11;
+ APP_OP_NEIGHBORING_CELLS = 12;
+ APP_OP_CALL_PHONE = 13;
+ APP_OP_READ_SMS = 14;
+ APP_OP_WRITE_SMS = 15;
+ APP_OP_RECEIVE_SMS = 16;
+ APP_OP_RECEIVE_EMERGENCY_SMS = 17;
+ APP_OP_RECEIVE_MMS = 18;
+ APP_OP_RECEIVE_WAP_PUSH = 19;
+ APP_OP_SEND_SMS = 20;
+ APP_OP_READ_ICC_SMS = 21;
+ APP_OP_WRITE_ICC_SMS = 22;
+ APP_OP_WRITE_SETTINGS = 23;
+ APP_OP_SYSTEM_ALERT_WINDOW = 24;
+ APP_OP_ACCESS_NOTIFICATIONS = 25;
+ APP_OP_CAMERA = 26;
+ APP_OP_RECORD_AUDIO = 27;
+ APP_OP_PLAY_AUDIO = 28;
+ APP_OP_READ_CLIPBOARD = 29;
+ APP_OP_WRITE_CLIPBOARD = 30;
+ APP_OP_TAKE_MEDIA_BUTTONS = 31;
+ APP_OP_TAKE_AUDIO_FOCUS = 32;
+ APP_OP_AUDIO_MASTER_VOLUME = 33;
+ APP_OP_AUDIO_VOICE_VOLUME = 34;
+ APP_OP_AUDIO_RING_VOLUME = 35;
+ APP_OP_AUDIO_MEDIA_VOLUME = 36;
+ APP_OP_AUDIO_ALARM_VOLUME = 37;
+ APP_OP_AUDIO_NOTIFICATION_VOLUME = 38;
+ APP_OP_AUDIO_BLUETOOTH_VOLUME = 39;
+ APP_OP_WAKE_LOCK = 40;
+ APP_OP_MONITOR_LOCATION = 41;
+ APP_OP_MONITOR_HIGH_POWER_LOCATION = 42;
+ APP_OP_GET_USAGE_STATS = 43;
+ APP_OP_MUTE_MICROPHONE = 44;
+ APP_OP_TOAST_WINDOW = 45;
+ APP_OP_PROJECT_MEDIA = 46;
+ APP_OP_ACTIVATE_VPN = 47;
+ APP_OP_WRITE_WALLPAPER = 48;
+ APP_OP_ASSIST_STRUCTURE = 49;
+ APP_OP_ASSIST_SCREENSHOT = 50;
+ APP_OP_READ_PHONE_STATE = 51;
+ APP_OP_ADD_VOICEMAIL = 52;
+ APP_OP_USE_SIP = 53;
+ APP_OP_PROCESS_OUTGOING_CALLS = 54;
+ APP_OP_USE_FINGERPRINT = 55;
+ APP_OP_BODY_SENSORS = 56;
+ APP_OP_READ_CELL_BROADCASTS = 57;
+ APP_OP_MOCK_LOCATION = 58;
+ APP_OP_READ_EXTERNAL_STORAGE = 59;
+ APP_OP_WRITE_EXTERNAL_STORAGE = 60;
+ APP_OP_TURN_SCREEN_ON = 61;
+ APP_OP_GET_ACCOUNTS = 62;
+ APP_OP_RUN_IN_BACKGROUND = 63;
+ APP_OP_AUDIO_ACCESSIBILITY_VOLUME = 64;
+ APP_OP_READ_PHONE_NUMBERS = 65;
+ APP_OP_REQUEST_INSTALL_PACKAGES = 66;
+ APP_OP_PICTURE_IN_PICTURE = 67;
+ APP_OP_INSTANT_APP_START_FOREGROUND = 68;
+ APP_OP_ANSWER_PHONE_CALLS = 69;
+ APP_OP_RUN_ANY_IN_BACKGROUND = 70;
+ APP_OP_CHANGE_WIFI_STATE = 71;
+ APP_OP_REQUEST_DELETE_PACKAGES = 72;
+ APP_OP_BIND_ACCESSIBILITY_SERVICE = 73;
+ APP_OP_ACCEPT_HANDOVER = 74;
+ APP_OP_MANAGE_IPSEC_TUNNELS = 75;
+ APP_OP_START_FOREGROUND = 76;
+ APP_OP_BLUETOOTH_SCAN = 77;
+ APP_OP_USE_BIOMETRIC = 78;
+ APP_OP_ACTIVITY_RECOGNITION = 79;
+ APP_OP_SMS_FINANCIAL_TRANSACTIONS = 80;
+ APP_OP_READ_MEDIA_AUDIO = 81;
+ APP_OP_WRITE_MEDIA_AUDIO = 82;
+ APP_OP_READ_MEDIA_VIDEO = 83;
+ APP_OP_WRITE_MEDIA_VIDEO = 84;
+ APP_OP_READ_MEDIA_IMAGES = 85;
+ APP_OP_WRITE_MEDIA_IMAGES = 86;
+ APP_OP_LEGACY_STORAGE = 87;
+ APP_OP_ACCESS_ACCESSIBILITY = 88;
+ APP_OP_READ_DEVICE_IDENTIFIERS = 89;
+ APP_OP_ACCESS_MEDIA_LOCATION = 90;
+ APP_OP_QUERY_ALL_PACKAGES = 91;
+ APP_OP_MANAGE_EXTERNAL_STORAGE = 92;
+ APP_OP_INTERACT_ACROSS_PROFILES = 93;
+ APP_OP_ACTIVATE_PLATFORM_VPN = 94;
+ APP_OP_LOADER_USAGE_STATS = 95;
+ APP_OP_ACCESS_CALL_AUDIO = 96;
+ APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
+ APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98;
+}
+
+
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 030483b..ef6eb38 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -724,6 +724,16 @@
// CATEGORY: SETTINGS
// OS: R
ACTION_ADB_WIRELESS_OFF = 1735;
+
+ // ACTION: Change Wi-Fi hotspot name
+ // CATEGORY: SETTINGS
+ // OS: R
+ ACTION_SETTINGS_CHANGE_WIFI_HOTSPOT_NAME = 1736;
+
+ // ACTION: Change Wi-Fi hotspot password
+ // CATEGORY: SETTINGS
+ // OS: R
+ ACTION_SETTINGS_CHANGE_WIFI_HOTSPOT_PASSWORD = 1737;
}
/**
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index 4a7d043..e8ae1b3 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -109,6 +109,7 @@
}
optional int32 network_security_config_res = 17;
optional int32 category = 18;
+ optional bool enable_gwp_asan = 19;
}
optional Detail detail = 17;
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index d6687f0..64cf75d 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -376,8 +376,7 @@
optional bytes system_trace = 3026 [
(section).type = SECTION_FILE,
(section).args = "/data/misc/perfetto-traces/incident-trace",
- (privacy).dest = DEST_AUTOMATIC,
- (section).userdebug_and_eng_only = true
+ (privacy).dest = DEST_AUTOMATIC
];
// Dropbox entries split by tags.
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 3d8108d..684a292 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -153,7 +153,7 @@
CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126;
SET_AUTO_TIME = 127;
SET_AUTO_TIME_ZONE = 128;
- SET_PACKAGES_PROTECTED = 129;
+ SET_USER_CONTROL_DISABLED_PACKAGES = 129;
SET_FACTORY_RESET_PROTECTION = 130;
SET_COMMON_CRITERIA_MODE = 131;
ALLOW_MODIFICATION_OF_ADMIN_CONFIGURED_NETWORKS = 132;
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index 4777169..f14e3ed 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -20,6 +20,43 @@
option java_outer_classname = "TelephonyProtoEnums";
option java_multiple_files = true;
+enum CallBearerEnum {
+ /** Call bearer is unknown or invalid */
+ CALL_BEARER_UNKNOWN = 0;
+
+ /** Call bearer is legacy CS */
+ CALL_BEARER_CS = 1;
+
+ /** Call bearer is IMS */
+ CALL_BEARER_IMS = 2;
+}
+
+enum CallDirectionEnum {
+ /** Call direction: unknown or invalid */
+ CALL_DIRECTION_UNKNOWN = 0;
+
+ /** Call direction: mobile originated (outgoing for this device) */
+ CALL_DIRECTION_MO = 1;
+
+ /** Call direction: mobile terminated (incoming for this device) */
+ CALL_DIRECTION_MT = 2;
+}
+
+// Call setup duration buckets.
+// See com.android.internal.telephony.metrics.VoiceCallSessionStats for definition.
+enum CallSetupDurationEnum {
+ CALL_SETUP_DURATION_UNKNOWN = 0;
+ CALL_SETUP_DURATION_EXTREMELY_FAST = 1;
+ CALL_SETUP_DURATION_ULTRA_FAST = 2;
+ CALL_SETUP_DURATION_VERY_FAST = 3;
+ CALL_SETUP_DURATION_FAST = 4;
+ CALL_SETUP_DURATION_NORMAL = 5;
+ CALL_SETUP_DURATION_SLOW = 6;
+ CALL_SETUP_DURATION_VERY_SLOW = 7;
+ CALL_SETUP_DURATION_ULTRA_SLOW = 8;
+ CALL_SETUP_DURATION_EXTREMELY_SLOW = 9;
+}
+
// Data conn. power states, primarily used by android/telephony/DataConnectionRealTimeInfo.java.
enum DataConnectionPowerStateEnum {
DATA_CONNECTION_POWER_STATE_LOW = 1;
@@ -63,7 +100,6 @@
SIGNAL_STRENGTH_GREAT = 4;
}
-
enum ServiceStateEnum {
/**
* Normal operation condition, the phone is registered
diff --git a/core/proto/android/util/quotatracker.proto b/core/proto/android/util/quotatracker.proto
index 5d022ed..d98e5ee 100644
--- a/core/proto/android/util/quotatracker.proto
+++ b/core/proto/android/util/quotatracker.proto
@@ -131,93 +131,3 @@
// Next tag: 4
}
-
-// A com.android.util.quota.DurationQuotaTracker object.
-message DurationQuotaTrackerProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional QuotaTrackerProto base_quota_data = 1;
-
- message DurationLimit {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional CategoryProto category = 1;
- optional int64 limit_ms = 2;
- optional int64 window_size_ms = 3;
- }
- repeated DurationLimit duration_limit = 2;
-
- message ExecutionStats {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // The time after which this record should be considered invalid (out of date), in the
- // elapsed realtime timebase.
- optional int64 expiration_time_elapsed = 1;
-
- optional int32 window_size_ms = 2;
- optional int64 duration_limit_ms = 3;
-
- // The overall session duration in the window.
- optional int64 session_duration_in_window_ms = 4;
- // The number of individual long-running events in the window.
- optional int32 event_count_in_window = 5;
-
- // The time after which the app will be under the bucket quota. This is only valid if
- // session_duration_in_window_ms >= duration_limit_ms.
- optional int64 in_quota_time_elapsed = 6;
- }
-
- message Timer {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // True if the Timer is actively tracking long-running events.
- optional bool is_active = 1;
- // The time this timer last became active. Only valid if is_active is true.
- optional int64 start_time_elapsed = 2;
- // How many long-running events are currently running. Valid only if is_active is true.
- optional int32 event_count = 3;
- }
-
- message TimingSession {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional int64 start_time_elapsed = 1;
- optional int64 end_time_elapsed = 2;
- // How many events started during this session. This only count long-running events, not
- // instantaneous events.
- optional int32 event_count = 3;
- }
-
- message UptcStats {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional UptcProto uptc = 1;
-
- // True if the UPTC has been given free quota.
- optional bool is_quota_free = 2;
-
- optional Timer timer = 3;
-
- repeated TimingSession saved_sessions = 4;
-
- repeated ExecutionStats execution_stats = 5;
- }
- repeated UptcStats uptc_stats = 3;
-
- message ReachedQuotaAlarmListener {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional int64 trigger_time_elapsed = 1;
-
- message UptcTimes {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional UptcProto uptc = 1;
- optional int64 out_of_quota_time_elapsed = 2;
- }
- repeated UptcTimes uptc_times = 2;
- }
- optional ReachedQuotaAlarmListener reached_quota_alarm_listener = 4;
-
- // Next tag: 5
-}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ec5159c..ad984e6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1109,13 +1109,12 @@
grants your app this permission. If you don't need this permission, be sure your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
targetSdkVersion}</a> is 4 or higher.
- <p>Protection level: dangerous
+ <p>Protection level: normal
-->
<permission android:name="android.permission.READ_PHONE_STATE"
- android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_readPhoneState"
android:description="@string/permdesc_readPhoneState"
- android:protectionLevel="dangerous" />
+ android:protectionLevel="normal" />
<!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
@@ -1689,6 +1688,10 @@
<permission android:name="android.permission.NETWORK_FACTORY"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows applications to access network stats provider -->
+ <permission android:name="android.permission.NETWORK_STATS_PROVIDER"
+ android:protectionLevel="signature" />
+
<!-- Allows Settings and SystemUI to call methods in Networking services
<p>Not for use by third-party or privileged applications.
@SystemApi @TestApi
@@ -1841,7 +1844,7 @@
and bypass OMAPI AccessControlEnforcer.
<p>Not for use by third-party applications.
@hide -->
- <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED"
+ <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION"
android:protectionLevel="signature|privileged" />
<!-- @deprecated This permission used to allow too broad access to sensitive methods and all its
@@ -3755,6 +3758,12 @@
<permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to an exempt an app from having its permission be
+ auto-revoked when unused for an extended period of time.
+ @hide -->
+ <permission android:name="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS"
+ android:protectionLevel="signature|installer" />
+
<!-- @hide Allows an application to observe permission changes. -->
<permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
android:protectionLevel="signature|privileged" />
@@ -5002,15 +5011,15 @@
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
- <!-- Feature Id for Country Detector. -->
- <feature android:featureId="CountryDetector" android:label="@string/country_detector"/>
- <!-- Feature Id for Location service. -->
- <feature android:featureId="LocationService" android:label="@string/location_service"/>
- <!-- Feature Id for Sensor Notification service. -->
- <feature android:featureId="SensorNotificationService"
+ <!-- Attribution for Country Detector. -->
+ <attribution android:tag="CountryDetector" android:label="@string/country_detector"/>
+ <!-- Attribution for Location service. -->
+ <attribution android:tag="LocationService" android:label="@string/location_service"/>
+ <!-- Attribution for Sensor Notification service. -->
+ <attribution android:tag="SensorNotificationService"
android:label="@string/sensor_notification_service"/>
<!-- Feature Id for Twilight service. -->
- <feature android:featureId="TwilightService" android:label="@string/twilight_service"/>
+ <attribution android:tag="TwilightService" android:label="@string/twilight_service"/>
<application android:process="system"
android:persistent="true"
@@ -5199,6 +5208,7 @@
<activity android:name="com.android.internal.app.BlockedAppActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
+ android:lockTaskMode="always"
android:process=":ui">
</activity>
diff --git a/core/res/res/drawable/conversation_badge_background.xml b/core/res/res/drawable/conversation_badge_background.xml
new file mode 100644
index 0000000..0dd0dcd
--- /dev/null
+++ b/core/res/res/drawable/conversation_badge_background.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid
+ android:color="#ffffff"/>
+
+ <size
+ android:width="26dp"
+ android:height="26dp"/>
+</shape>
+
diff --git a/core/res/res/drawable/ic_collapse_notification.xml b/core/res/res/drawable/ic_collapse_notification.xml
index 124e99e..ca4f0ed 100644
--- a/core/res/res/drawable/ic_collapse_notification.xml
+++ b/core/res/res/drawable/ic_collapse_notification.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2015 The Android Open Source Project
+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.
@@ -15,11 +15,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14.0dp"
- android:height="14.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:width="22.0dp"
+ android:height="22.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
- android:pathData="M12.0,8.0l-6.0,6.0l1.4,1.4l4.6,-4.6l4.6,4.6L18.0,14.0L12.0,8.0z"/>
-</vector>
+ android:pathData="M18.59,16.41L20.0,15.0l-8.0,-8.0 -8.0,8.0 1.41,1.41L12.0,9.83"/>
+ <path
+ android:pathData="M0 0h24v24H0V0z"
+ android:fillColor="#00000000"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_expand_notification.xml b/core/res/res/drawable/ic_expand_notification.xml
index 847e326..a080ce4 100644
--- a/core/res/res/drawable/ic_expand_notification.xml
+++ b/core/res/res/drawable/ic_expand_notification.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2015 The Android Open Source Project
+Copyright (C) 2014 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.
@@ -15,11 +15,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14.0dp"
- android:height="14.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:width="22.0dp"
+ android:height="22.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
- android:pathData="M16.6,8.6L12.0,13.2L7.4,8.6L6.0,10.0l6.0,6.0l6.0,-6.0L16.6,8.6z"/>
-</vector>
+ android:pathData="M5.41,7.59L4.0,9.0l8.0,8.0 8.0,-8.0 -1.41,-1.41L12.0,14.17"/>
+ <path
+ android:pathData="M24 24H0V0h24v24z"
+ android:fillColor="#00000000"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/tab_indicator_resolver.xml b/core/res/res/drawable/tab_indicator_resolver.xml
index ff16d81a..f97773e 100644
--- a/core/res/res/drawable/tab_indicator_resolver.xml
+++ b/core/res/res/drawable/tab_indicator_resolver.xml
@@ -25,7 +25,7 @@
</item>
<item android:gravity="bottom">
<shape android:shape="rectangle"
- android:tint="@color/resolver_tabs_active_color">
+ android:tint="?attr/colorAccent">
<size android:height="2dp" />
<solid android:color="@color/tab_indicator_material" />
</shape>
diff --git a/core/res/res/layout-car/car_resolver_different_item_header.xml b/core/res/res/layout-car/car_resolver_different_item_header.xml
deleted file mode 100644
index 222ecc6..0000000
--- a/core/res/res/layout-car/car_resolver_different_item_header.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 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.
- */
--->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alwaysShow="true"
- android:text="@*android:string/use_a_different_app"
- android:minHeight="56dp"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:gravity="start|center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:elevation="8dp"
-/>
\ No newline at end of file
diff --git a/core/res/res/layout-car/car_resolver_list.xml b/core/res/res/layout-car/car_resolver_list.xml
index 15a8645..755cbfe 100644
--- a/core/res/res/layout-car/car_resolver_list.xml
+++ b/core/res/res/layout-car/car_resolver_list.xml
@@ -23,90 +23,142 @@
android:id="@id/contentPanel">
<LinearLayout
- android:id="@+id/button_bar"
- android:visibility="gone"
- style="?attr/buttonBarStyle"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_ignoreOffset="true"
- android:layout_alwaysShow="true"
- android:layout_hasNestedScrollIndicator="true"
- android:background="?attr/colorBackgroundFloating"
- android:orientation="horizontal"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="12dp"
+ android:layout_height="match_parent"
android:weightSum="5"
- android:paddingEnd="12dp"
+ android:layout_alwaysShow="true"
+ android:orientation="vertical"
+ android:background="?attr/colorBackgroundFloating"
android:elevation="8dp">
- <TextView
- android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:textSize="40sp"
- android:layout_weight="5"
- android:layout_gravity = "left"
+ <LinearLayout
+ android:id="@+id/button_bar"
android:visibility="gone"
- android:textColor="?attr/colorAccent"
- android:singleLine="true"/>
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
+ style="?attr/buttonBarStyle"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="56dp"
- android:layout_gravity = "left"
- android:layout_weight="3"
+ android:layout_ignoreOffset="true"
+ android:layout_alwaysShow="true"
+ android:layout_hasNestedScrollIndicator="true"
+ android:background="?attr/colorBackgroundFloating"
+ android:orientation="horizontal"
android:paddingTop="8dp"
- android:layout_below="@id/profile_button"
- android:paddingBottom="8dp"/>
+ android:paddingStart="12dp"
+ android:weightSum="4"
+ android:paddingEnd="12dp"
+ android:elevation="8dp">
- <Button
- android:id="@+id/button_once"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:layout_gravity = "right"
- android:text="@string/activity_resolver_use_once"
- android:layout_weight="1"
- android:onClick="onButtonClick"/>
+ <TextView
+ android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:textSize="40sp"
+ android:layout_weight="4"
+ android:layout_gravity="left"
+ android:visibility="gone"
+ android:textColor="?attr/colorAccent"
+ android:singleLine="true"/>
- <Button
- android:id="@+id/button_always"
- android:layout_marginLeft="10dp"
- android:layout_width="wrap_content"
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:layout_weight="3"
+ android:paddingTop="8dp"
+ android:layout_below="@id/profile_button"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:paddingBottom="8dp"/>
+
+ <Button
+ android:id="@+id/button_once"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:layout_gravity="right"
+ style="?attr/buttonBarButtonStyle"
+ android:text="@string/activity_resolver_use_once"
+ android:layout_weight="0.5"
+ android:onClick="onButtonClick"/>
+
+ <Button
+ android:id="@+id/button_always"
+ android:layout_marginLeft="2dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:layout_gravity="right"
+ style="?attr/buttonBarButtonStyle"
+ android:text="@string/activity_resolver_use_always"
+ android:layout_weight="0.5"
+ android:onClick="onButtonClick"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/stub"
+ android:visibility="gone"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:enabled="false"
- android:layout_gravity = "right"
- android:text="@string/activity_resolver_use_always"
- android:layout_weight="1"
- android:onClick="onButtonClick"/>
+ android:background="?attr/colorBackgroundFloating"/>
+
+ <TabHost
+ android:id="@+id/profile_tabhost"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:background="?attr/colorBackgroundFloating">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ </TabWidget>
+ <View
+ android:id="@+id/resolver_tab_divider"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical"
+ android:layout_marginBottom="8dp"/>
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.internal.app.ResolverViewPager
+ android:id="@+id/profile_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
+
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical"/>
+
+ <TextView android:id="@+id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/colorBackgroundFloating"
+ android:elevation="8dp"
+ android:layout_alwaysShow="true"
+ android:text="@string/noApplications"
+ android:padding="32dp"
+ android:gravity="center"
+ android:visibility="gone"/>
+
</LinearLayout>
- <ListView
- android:layout_width="match_parent"
- android:layout_height="500dp"
- android:id="@+id/resolver_list"
- android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
- android:nestedScrollingEnabled="true"
- android:scrollIndicators="top|bottom"/>
-
- <TextView android:id="@+id/empty"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
- android:layout_alwaysShow="true"
- android:text="@string/noApplications"
- android:padding="32dp"
- android:gravity="center"
- android:visibility="gone"/>
-
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout-car/car_resolver_list_with_default.xml b/core/res/res/layout-car/car_resolver_list_with_default.xml
index 2aed00b..5e450b2 100644
--- a/core/res/res/layout-car/car_resolver_list_with_default.xml
+++ b/core/res/res/layout-car/car_resolver_list_with_default.xml
@@ -40,12 +40,12 @@
<ImageView
android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="60dp"
+ android:layout_height="60dp"
android:layout_gravity="start|top"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="20dp"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="5dp"
+ android:layout_marginTop="10dp"
android:src="@drawable/resolver_icon_placeholder"
android:scaleType="fitCenter"/>
@@ -55,7 +55,7 @@
android:layout_weight="1"
android:layout_height="?attr/listPreferredItemHeight"
android:layout_marginStart="16dp"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="start|center_vertical"
android:paddingEnd="16dp"/>
@@ -120,7 +120,7 @@
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
+ style="?attr/buttonBarButtonStyle"
android:minHeight="@dimen/alert_dialog_button_bar_height"
android:layout_height="wrap_content"
android:enabled="false"
@@ -133,29 +133,64 @@
android:layout_gravity="end"
android:maxLines="2"
android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
android:layout_height="wrap_content"
android:enabled="false"
android:text="@string/activity_resolver_use_always"
android:onClick="onButtonClick"/>
</LinearLayout>
+ <FrameLayout
+ android:id="@+id/stub"
+ android:layout_alwaysShow="true"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/colorBackgroundFloating"/>
+
+ <TabHost
+ android:layout_alwaysShow="true"
+ android:id="@+id/profile_tabhost"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:background="?attr/colorBackgroundFloating">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ </TabWidget>
+ <View
+ android:id="@+id/resolver_tab_divider"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical"
+ android:layout_marginBottom="8dp"/>
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.internal.app.ResolverViewPager
+ android:id="@+id/profile_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ </com.android.internal.app.ResolverViewPager>
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
+
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/dividerVertical"/>
-
- <ListView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/resolver_list"
- android:layout_weight="4"
- android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
- android:nestedScrollingEnabled="true"
- android:divider="@null"/>
</LinearLayout>
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/conversation_face_pile_layout.xml b/core/res/res/layout/conversation_face_pile_layout.xml
new file mode 100644
index 0000000..1db3870
--- /dev/null
+++ b/core/res/res/layout/conversation_face_pile_layout.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/conversation_face_pile"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:forceHasOverlappingRendering="false"
+ >
+ <ImageView
+ android:id="@+id/conversation_face_pile_top"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="centerCrop"
+ android:layout_gravity="end|top"
+ />
+ <FrameLayout
+ android:id="@+id/conversation_face_pile_bottom_background"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_gravity="start|bottom"
+ android:background="@drawable/conversation_badge_background">
+ <ImageView
+ android:id="@+id/conversation_face_pile_bottom"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="centerCrop"
+ android:layout_gravity="center"
+ />
+ </FrameLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index f5fa1b6a..6f36aae 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -91,7 +91,6 @@
android:textAppearance="@style/TextAppearance.Material.Notification.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:showRelative="true"
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
new file mode 100644
index 0000000..2348dee
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -0,0 +1,206 @@
+<?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
+ -->
+<com.android.internal.widget.ConversationLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="conversation"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <FrameLayout
+ android:layout_width="@dimen/conversation_content_start"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingTop="12dp"
+ >
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal"
+ >
+
+ <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
+ <ImageView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no"
+ />
+
+ <ViewStub
+ android:layout="@layout/conversation_face_pile_layout"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:id="@+id/conversation_face_pile"
+ />
+
+ <FrameLayout
+ android:id="@+id/conversation_icon_badge"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:layout_marginLeft="@dimen/conversation_badge_side_margin"
+ android:layout_marginTop="@dimen/conversation_badge_side_margin"
+ android:background="@drawable/conversation_badge_background" >
+ <!-- Badge: 20x20, 48dp padding left + top -->
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/conversation_icon_size_badged"
+ android:layout_height="@dimen/conversation_icon_size_badged"
+ android:layout_gravity="center"
+ />
+ </FrameLayout>
+ </FrameLayout>
+ </FrameLayout>
+
+ <!-- Wraps entire "expandable" notification -->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+ <!-- LinearLayout for Expand Button-->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/expand_button_and_content_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="start|top"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc-->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <!-- Header -->
+ <LinearLayout
+ android:id="@+id/conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="16dp"
+ android:paddingStart="@dimen/conversation_content_start"
+ >
+ <TextView
+ android:id="@+id/conversation_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textSize="16sp"
+ android:singleLine="true"
+ />
+
+ <TextView
+ android:id="@+id/time_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:layout_gravity="center"
+ android:paddingTop="1sp"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <DateTimeView
+ android:id="@+id/time"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:paddingTop="1sp"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/profile_badge"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
+ />
+ </LinearLayout>
+
+ <!-- Messages -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:spacing="@dimen/notification_messaging_spacing"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+ <!-- Unread Count -->
+ <!-- <TextView /> -->
+
+ <!-- This is where the expand button will be placed when collapsed-->
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/conversation_content_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ <include layout="@layout/notification_material_action_list" />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <!--This is dynamically placed between here and at the end of the layout-->
+ <FrameLayout
+ android:id="@+id/expand_button_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/conversation_expand_button_expanded_size"
+ android:layout_gravity="end|top"
+ android:paddingStart="16dp"
+ android:paddingEnd="@dimen/notification_content_margin_end">
+ <com.android.internal.widget.NotificationExpandButton
+ android:id="@+id/expand_button"
+ android:layout_width="@dimen/notification_header_expand_icon_size"
+ android:layout_height="@dimen/notification_header_expand_icon_size"
+ android:layout_gravity="center"
+ android:drawable="@drawable/ic_expand_notification"
+ android:clickable="false"
+ android:importantForAccessibility="no"
+ />
+ </FrameLayout>
+</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 483b479..3188861 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -20,14 +20,20 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
- <ImageView
- android:id="@+id/message_icon"
- android:layout_width="@dimen/messaging_avatar_size"
- android:layout_height="@dimen/messaging_avatar_size"
- android:layout_marginEnd="12dp"
- android:scaleType="centerCrop"
- android:importantForAccessibility="no" />
+ <FrameLayout
+ android:id="@+id/message_icon_container"
+ android:layout_width="@dimen/conversation_content_start"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:layout_gravity="top|center_horizontal"
+ android:id="@+id/message_icon"
+ android:layout_width="@dimen/messaging_avatar_size"
+ android:layout_height="@dimen/messaging_avatar_size"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no" />
+ </FrameLayout>
<com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/messaging_group_content_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -43,7 +49,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_text_margin_top"
- android:spacing="2dp"/>
+ android:spacing="2dp" />
</com.android.internal.widget.RemeasuringLinearLayout>
<FrameLayout
android:id="@+id/messaging_group_icon_container"
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 619b392..5fdf190 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textColor="@color/resolver_empty_state_text"
- android:textSize="18sp"
+ android:textSize="14sp"
android:layout_centerHorizontal="true" />
<TextView
android:id="@+id/resolver_empty_state_subtitle"
@@ -47,7 +47,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/resolver_empty_state_text"
- android:textSize="14sp"
+ android:textSize="12sp"
android:gravity="center_horizontal"
android:layout_centerHorizontal="true" />
<Button
@@ -60,7 +60,7 @@
android:background="@null"
android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textSize="14sp"
- android:textColor="@color/resolver_tabs_active_color"
+ android:textColor="?attr/colorAccent"
android:layout_centerHorizontal="true" />
<ProgressBar
android:id="@+id/resolver_empty_state_progress"
@@ -71,5 +71,5 @@
android:indeterminate="true"
android:layout_centerHorizontal="true"
android:layout_below="@+id/resolver_empty_state_subtitle"
- android:indeterminateTint="@color/resolver_tabs_active_color"/>
+ android:indeterminateTint="?attr/colorAccent"/>
</RelativeLayout>
\ No newline at end of file
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 7f77e6c..708b4f3 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -33,7 +33,6 @@
<color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color>
<color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
- <color name="resolver_tabs_active_color">#FF8AB4F8</color>
<color name="resolver_empty_state_text">#FFFFFF</color>
<color name="resolver_empty_state_icon">#FFFFFF</color>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 7a5ab2d..93b2063 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1538,6 +1538,28 @@
<flag name="microphone" value="0x80" />
</attr>
+ <!-- Enable sampled memory bug detection in this process.
+ When enabled, a very small, random subset of native
+ memory allocations are protected with guard pages, providing an
+ ASan-like error report in case of a memory corruption bug.
+
+ GWP-ASan is a recursive acronym. It stands for “GWP-ASan Will Provide Allocation SANity”.
+ See the <a href="http://llvm.org/docs/GwpAsan.html">LLVM documentation</a>
+ for more information about this feature.
+
+ <p>This tag can be applied to any tag that allows
+ {@link android.R.styleable#AndroidManifestProcess process} tag:
+ {@link android.R.styleable#AndroidManifestApplication application} tag (to supply
+ a default setting for all application components), or with the
+ {@link android.R.styleable#AndroidManifestProvider provider},
+ {@link android.R.styleable#AndroidManifestService service},
+ {@link android.R.styleable#AndroidManifestReceiver receiver},
+ {@link android.R.styleable#AndroidManifestActivity activity} tag.
+ When multiple components run in the same process,
+ the first component to start determines the behavior of the entire process.
+
+ The default value is {@code false}. -->
+ <attr name="enableGwpAsan" format="boolean" />
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
@@ -1549,7 +1571,7 @@
<code>com.google.app.<em>appname</em></code>
<p>Inside of the manifest tag, may appear the following tags
- in any order: {@link #AndroidManifestFeature feature},
+ in any order: {@link #AndroidManifestAttribution attribution},
{@link #AndroidManifestPermission permission},
{@link #AndroidManifestPermissionGroup permission-group},
{@link #AndroidManifestPermissionTree permission-tree},
@@ -1803,31 +1825,51 @@
The default value is {@code true}. -->
<attr name="allowNativeHeapPointerTagging" format="boolean" />
+
+ <attr name="enableGwpAsan" />
+
+ <!-- If {@code true} allow requesting that its permissions don't get automatically
+ revoked when the app is unused for an extended amount of time.
+
+ The default value is {@code false}. -->
+ <attr name="requestDontAutoRevokePermissions" format="boolean" />
+
+ <!-- If {@code true} its permissions shouldn't get automatically
+ revoked when the app is unused for an extended amount of time.
+
+ This implies {@code requestDontAutoRevokePermissions=true}
+
+ The default value is {@code false}. -->
+ <attr name="allowDontAutoRevokePermissions" format="boolean" />
</declare-styleable>
- <!-- 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
+ <!-- An attribution is a logical part of an app and is identified by a tag.
+ E.g. a photo sharing app might include a direct messaging component. To tag certain code as
+ belonging to an attribution, use a context created via
+ {@link android.content.Context#createAttributionContext(String)} for any interaction with the
system.
<p>This appears as a child tag of the root {@link #AndroidManifest manifest} tag.
- <p>In case this feature inherits from another feature, this tag can contain one or multiple
- {@link #AndroidManifestFeatureInheritFrom inherit-from} tags. -->
- <declare-styleable name="AndroidManifestFeature" parent="AndroidManifest">
- <!-- Required identifier for a feature. Can be passed to
- {@link android.content.Context#createFeatureContext} to create a context for this feature
- -->
+ <p>In case this attribution inherits from another attribution, this tag can contain one or
+ multiple {@link #AndroidManifestAttributionInheritFrom inherit-from} tags. -->
+ <declare-styleable name="AndroidManifestAttribution" parent="AndroidManifest">
+ <!-- TODO moltmann: Remove -->
<attr name="featureId" format="string" />
- <!-- Required user visible label for a feature. -->
+ <!-- Required identifier for a attribution. Can be passed to
+ {@link android.content.Context#createAttributionContext} to create a context tagged with
+ this attribution
+ -->
+ <attr name="tag" format="string" />
+ <!-- Required user visible label for a attribution. -->
<attr name="label" format="string" />
</declare-styleable>
<!-- Declares previously declared features this feature inherits from. -->
- <declare-styleable name="AndroidManifestFeatureInheritFrom" parent="AndroidManifestFeature">
- <!-- Identifier of the feature this feature inherits from -->
- <attr name="featureId" format="string" />
+ <declare-styleable name="AndroidManifestAttributionInheritFrom"
+ parent="AndroidManifestAttribution">
+ <!-- Identifier of the attribution this attribution inherits from -->
+ <attr name="tag" format="string" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
@@ -2309,6 +2351,7 @@
<declare-styleable name="AndroidManifestProcess" parent="AndroidManifestProcesses">
<!-- Required name of the process that is allowed -->
<attr name="process" />
+ <attr name="enableGwpAsan" />
</declare-styleable>
<!-- The <code>deny-permission</code> tag specifies that a permission is to be denied
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index bdec096..91248f1 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -224,8 +224,6 @@
<!-- Resolver/Chooser -->
<color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
- <color name="resolver_tabs_active_color">#FF1A73E8</color>
- <color name="resolver_tabs_inactive_color">#FF80868B</color>
<color name="resolver_empty_state_text">#FF202124</color>
<color name="resolver_empty_state_icon">#FF5F6368</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 48f0ef7..94764ca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2978,6 +2978,10 @@
<!-- Whether to use voip audio mode for ims call -->
<bool name="config_use_voip_mode_for_ims">false</bool>
+ <!-- Boolean indicating USSD over IMS is allowed.
+ If it is not supported due to modem limitations, USSD send over the CS pipe instead.-->
+ <bool name="config_allow_ussd_over_ims">false</bool>
+
<!-- String array containing numbers that shouldn't be logged. Country-specific. -->
<string-array name="unloggable_phone_numbers" />
@@ -4409,4 +4413,11 @@
<!-- Package name of custom session policy provider class used by MediaSessionService. -->
<string name="config_customSessionPolicyProvider"></string>
+
+ <!-- The max scale for the wallpaper when it's zoomed in -->
+ <item name="config_wallpaperMaxScale" format="float" type="dimen">1.15</item>
+
+ <!-- Package name that will receive an explicit manifest broadcast for
+ android.os.action.POWER_SAVE_MODE_CHANGED. -->
+ <string name="config_powerSaveModeChangedListenerPackage" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2faa0c9..4dedc63 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -682,7 +682,31 @@
<!-- The size of the right icon image when on low ram -->
<dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
- <dimen name="messaging_avatar_size">52dp</dimen>
+ <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
+ <dimen name="conversation_avatar_size">52dp</dimen>
+ <!-- Start of the content in the conversation template -->
+ <dimen name="conversation_content_start">80dp</dimen>
+ <!-- Size of the expand button when expanded -->
+ <dimen name="conversation_expand_button_expanded_size">80dp</dimen>
+ <!-- Top margin of the expand button for conversations when expanded -->
+ <dimen name="conversation_expand_button_top_margin_expanded">18dp</dimen>
+ <!-- Side margins of the conversation badge in relation to the conversation icon -->
+ <dimen name="conversation_badge_side_margin">36dp</dimen>
+ <!-- size of the notification icon when badged in a conversation -->
+ <dimen name="conversation_icon_size_badged">15dp</dimen>
+ <!-- size of the notification icon when centered in a conversation -->
+ <dimen name="conversation_icon_size_centered">20dp</dimen>
+ <!-- margin on the top when the icon is centered for group conversations -->
+ <dimen name="conversation_icon_margin_top_centered">5dp</dimen>
+
+ <!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end -->
+ <dimen name="conversation_header_expanded_padding_end">38dp</dimen>
+
+ <!-- margin at the end of messaging group icons when not conversations -->
+ <dimen name="messaging_layout_margin_end">12dp</dimen>
+
+ <!-- Padding between text and sender when singleline -->
+ <dimen name="messaging_group_singleline_sender_padding_end">4dp</dimen>
<dimen name="messaging_group_sending_progress_size">24dp</dimen>
@@ -776,6 +800,7 @@
<dimen name="resolver_empty_state_height_with_tabs">268dp</dimen>
<dimen name="resolver_max_collapsed_height">192dp</dimen>
<dimen name="resolver_max_collapsed_height_with_tabs">248dp</dimen>
+ <dimen name="resolver_tab_text_size">14sp</dimen>
<dimen name="chooser_action_button_icon_size">18dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 39e8197..cf68aff 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3004,6 +3004,7 @@
<public name="animatedImageDrawable"/>
<public name="htmlDescription"/>
<public name="preferMinimalPostProcessing"/>
+ <!-- @removed -->
<public name="featureId" />
<public name="supportsInlineSuggestions" />
<public name="crossProfile" />
@@ -3013,8 +3014,11 @@
<!-- @hide @SystemApi -->
<public name="minExtensionVersion" />
<public name="allowNativeHeapPointerTagging" />
+ <public name="requestDontAutoRevokePermissions" />
+ <public name="allowDontAutoRevokePermissions" />
<public name="preserveLegacyExternalStorage" />
<public name="mimeGroup" />
+ <public name="enableGwpAsan" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
@@ -3040,8 +3044,6 @@
<public name="config_defaultCallScreening" />
<!-- @hide @SystemApi @TestApi -->
<public name="config_systemGallery" />
- <!-- @hide @SystemApi -->
- <public name="low_memory" />
</public-group>
<public-group type="bool" first-id="0x01110005">
@@ -3069,12 +3071,6 @@
<public-group type="array" first-id="0x01070006">
<!-- @hide @SystemApi -->
<public name="simColors" />
- <!-- @hide @SystemApi -->
- <public name="config_restrictedPreinstalledCarrierApps" />
- <!-- @hide @SystemApi -->
- <public name="config_sms_enabled_single_shift_tables" />
- <!-- @hide @SystemApi -->
- <public name="config_sms_enabled_locking_shift_tables" />
</public-group>
<public-group type="string" first-id="0x0104002c">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 789628d..26024ed 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1169,7 +1169,7 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
<string name="permlab_cameraOpenCloseListener">Allow an application or service to receive callbacks about camera devices being opened or closed.</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
- <string name="permdesc_cameraOpenCloseListener">This signature app can receive callbacks when any camera device is being opened (by what application package) or closed.</string>
+ <string name="permdesc_cameraOpenCloseListener">This app can receive callbacks when any camera device is being opened (by what application) or closed.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_vibrate">control vibration</string>
@@ -2985,72 +2985,6 @@
<!-- Title for EditText context menu [CHAR LIMIT=20] -->
<string name="editTextMenuTitle">Text actions</string>
- <!-- Label for item in the text selection menu to trigger an Email app. Should be a verb. [CHAR LIMIT=30] -->
- <string name="email">Email</string>
-
- <!-- Accessibility description for an item in the text selection menu to trigger an Email app [CHAR LIMIT=NONE] -->
- <string name="email_desc">Email selected address</string>
-
- <!-- Label for item in the text selection menu to trigger a Dialer app. Should be a verb. [CHAR LIMIT=30] -->
- <string name="dial">Call</string>
-
- <!-- Accessibility description for an item in the text selection menu to call a phone number [CHAR LIMIT=NONE] -->
- <string name="dial_desc">Call selected phone number</string>
-
- <!-- Label for item in the text selection menu to trigger a Map app. Should be a verb. [CHAR LIMIT=30] -->
- <string name="map">Map</string>
-
- <!-- Accessibility description for an item in the text selection menu to open maps for an address [CHAR LIMIT=NONE] -->
- <string name="map_desc">Locate selected address</string>
-
- <!-- Label for item in the text selection menu to trigger a Browser app. Should be a verb. [CHAR LIMIT=30] -->
- <string name="browse">Open</string>
-
- <!-- Accessibility description for an item in the text selection menu to open a URL in a browser [CHAR LIMIT=NONE] -->
- <string name="browse_desc">Open selected URL</string>
-
- <!-- Label for item in the text selection menu to trigger an SMS app. Should be a verb. [CHAR LIMIT=30] -->
- <string name="sms">Message</string>
-
- <!-- Accessibility description for an item in the text selection menu to send an SMS to a phone number [CHAR LIMIT=NONE] -->
- <string name="sms_desc">Message selected phone number</string>
-
- <!-- Label for item in the text selection menu to trigger adding a contact. Should be a verb. [CHAR LIMIT=30] -->
- <string name="add_contact">Add</string>
-
- <!-- Accessibility description for an item in the text selection menu to add the selected detail to contacts [CHAR LIMIT=NONE] -->
- <string name="add_contact_desc">Add to contacts</string>
-
- <!-- Label for item in the text selection menu to view the calendar for the selected time/date. Should be a verb. [CHAR LIMIT=30] -->
- <string name="view_calendar">View</string>
-
- <!-- Accessibility description for an item in the text selection menu to view the calendar for a date [CHAR LIMIT=NONE]-->
- <string name="view_calendar_desc">View selected time in calendar</string>
-
- <!-- Label for item in the text selection menu to create a calendar event at the selected time/date. Should be a verb. [CHAR LIMIT=30] -->
- <string name="add_calendar_event">Schedule</string>
-
- <!-- Accessibility description for an item in the text selection menu to schedule an event for a date [CHAR LIMIT=NONE] -->
- <string name="add_calendar_event_desc">Schedule event for selected time</string>
-
- <!-- Label for item in the text selection menu to track a selected flight number. Should be a verb. [CHAR LIMIT=30] -->
- <string name="view_flight">Track</string>
-
- <!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
- <string name="view_flight_desc">Track selected flight</string>
-
- <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] -->
- <string name="translate">Translate</string>
-
- <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] -->
- <string name="translate_desc">Translate selected text</string>
-
- <!-- Label for item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=30] -->
- <string name="define">Define</string>
-
- <!-- Accessibility description for an item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=NONE] -->
- <string name="define_desc">Define selected text</string>
-
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Storage space running out</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
@@ -5440,6 +5374,15 @@
<string name="as_app_forced_to_restricted_bucket">
<xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string>
+ <!-- The way a conversation name is displayed when single line. The text will be displayed to the end of this text with some spacing -->
+ <string name="conversation_single_line_name_display"><xliff:g id="sender_name" example="Sara">%1$s</xliff:g>:</string>
+
+ <!-- Conversation Title fallback if the there is no name provided in a 1:1 conversation [CHAR LIMIT=40]-->
+ <string name="conversation_title_fallback_one_to_one">Conversation</string>
+
+ <!-- Conversation Title fallback if the there is no name provided in a group chat conversation [CHAR LIMIT=40]-->
+ <string name="conversation_title_fallback_group_chat">Group Conversation</string>
+
<!-- ResolverActivity - profile tabs -->
<!-- Label of a tab on a screen. A user can tap this tap to switch to the 'Personal' view (that shows their personal content) if they have a work profile on their device. [CHAR LIMIT=NONE] -->
<string name="resolver_personal_tab">Personal</string>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 966f495..64768cf 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -146,7 +146,7 @@
<item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
</style>
<style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName">
- <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.MessagingName</item>
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item>
</style>
<style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/>
<style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
@@ -290,9 +290,6 @@
<style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
<item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
</style>
- <style name="TextAppearance.DeviceDefault.Notification.MessagingName" parent="TextAppearance.DeviceDefault.Notification.Title">
- <item name="textSize">16sp</item>
- </style>
<style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply">
<item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 63ac0e6..2415837 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -504,7 +504,7 @@
<style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Notification.Text">
<item name="layout_width">wrap_content</item>
<item name="layout_height">wrap_content</item>
- <item name="ellipsize">end</item>z
+ <item name="ellipsize">end</item>
</style>
<style name="Widget.Material.Notification.MessagingName" parent="Widget.Material.Light.TextView">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f1b87f7..a1b00f2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -551,28 +551,6 @@
<java-symbol type="string" name="replace" />
<java-symbol type="string" name="undo" />
<java-symbol type="string" name="redo" />
- <java-symbol type="string" name="email" />
- <java-symbol type="string" name="email_desc" />
- <java-symbol type="string" name="dial" />
- <java-symbol type="string" name="dial_desc" />
- <java-symbol type="string" name="map" />
- <java-symbol type="string" name="map_desc" />
- <java-symbol type="string" name="browse" />
- <java-symbol type="string" name="browse_desc" />
- <java-symbol type="string" name="sms" />
- <java-symbol type="string" name="sms_desc" />
- <java-symbol type="string" name="add_contact" />
- <java-symbol type="string" name="add_contact_desc" />
- <java-symbol type="string" name="view_calendar" />
- <java-symbol type="string" name="view_calendar_desc" />
- <java-symbol type="string" name="add_calendar_event" />
- <java-symbol type="string" name="add_calendar_event_desc" />
- <java-symbol type="string" name="view_flight" />
- <java-symbol type="string" name="view_flight_desc" />
- <java-symbol type="string" name="translate" />
- <java-symbol type="string" name="translate_desc" />
- <java-symbol type="string" name="define" />
- <java-symbol type="string" name="define_desc" />
<java-symbol type="string" name="textSelectionCABTitle" />
<java-symbol type="string" name="BaMmi" />
<java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -2553,6 +2531,7 @@
<java-symbol type="bool" name="config_device_wfc_ims_available" />
<java-symbol type="bool" name="config_carrier_wfc_ims_available" />
<java-symbol type="bool" name="config_use_voip_mode_for_ims" />
+ <java-symbol type="bool" name="config_allow_ussd_over_ims" />
<java-symbol type="attr" name="touchscreenBlocksFocus" />
<java-symbol type="layout" name="resolver_list_with_default" />
<java-symbol type="string" name="activity_resolver_set_always" />
@@ -3614,6 +3593,7 @@
<java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
<java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
<java-symbol type="string" name="config_batterySaverScheduleProvider" />
+ <java-symbol type="string" name="config_powerSaveModeChangedListenerPackage" />
<!-- For car devices -->
<java-symbol type="string" name="car_loading_profile" />
@@ -3875,9 +3855,35 @@
<java-symbol type="array" name="config_defaultImperceptibleKillingExemptionPkgs" />
<java-symbol type="array" name="config_defaultImperceptibleKillingExemptionProcStates" />
+ <java-symbol type="string" name="conversation_single_line_name_display" />
+ <java-symbol type="string" name="conversation_title_fallback_one_to_one" />
+ <java-symbol type="string" name="conversation_title_fallback_group_chat" />
+ <java-symbol type="id" name="conversation_icon" />
+ <java-symbol type="id" name="conversation_icon_badge" />
+ <java-symbol type="id" name="expand_button_container" />
+ <java-symbol type="id" name="messaging_group_content_container" />
+ <java-symbol type="id" name="expand_button_and_content_container" />
+ <java-symbol type="id" name="conversation_header" />
+ <java-symbol type="id" name="conversation_face_pile_bottom_background" />
+ <java-symbol type="id" name="conversation_face_pile_bottom" />
+ <java-symbol type="id" name="conversation_face_pile_top" />
+ <java-symbol type="id" name="conversation_face_pile" />
+ <java-symbol type="id" name="conversation_text" />
+ <java-symbol type="id" name="message_icon_container" />
+ <java-symbol type="dimen" name="conversation_expand_button_top_margin_expanded" />
+ <java-symbol type="dimen" name="conversation_expand_button_expanded_size" />
+ <java-symbol type="dimen" name="messaging_group_singleline_sender_padding_end" />
+ <java-symbol type="dimen" name="conversation_badge_side_margin" />
+ <java-symbol type="dimen" name="conversation_icon_size_badged" />
+ <java-symbol type="dimen" name="conversation_icon_size_centered" />
+ <java-symbol type="dimen" name="conversation_icon_margin_top_centered" />
+ <java-symbol type="dimen" name="conversation_content_start" />
+ <java-symbol type="dimen" name="messaging_layout_margin_end" />
+ <java-symbol type="dimen" name="conversation_header_expanded_padding_end" />
+ <java-symbol type="layout" name="notification_template_material_conversation" />
+ <java-symbol type="layout" name="conversation_face_pile_layout" />
+
<!-- Intent resolver and share sheet -->
- <java-symbol type="color" name="resolver_tabs_active_color" />
- <java-symbol type="color" name="resolver_tabs_inactive_color" />
<java-symbol type="string" name="resolver_personal_tab" />
<java-symbol type="string" name="resolver_personal_tab_accessibility" />
<java-symbol type="string" name="resolver_work_tab" />
@@ -3908,6 +3914,7 @@
<java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" />
<java-symbol type="dimen" name="resolver_max_collapsed_height_with_tabs" />
<java-symbol type="bool" name="sharesheet_show_content_preview" />
+ <java-symbol type="dimen" name="resolver_tab_text_size" />
<!-- Toast message for background started foreground service while-in-use permission restriction feature -->
<java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
@@ -3925,4 +3932,6 @@
<java-symbol type="string" name="config_customMediaKeyDispatcher" />
<java-symbol type="string" name="config_customSessionPolicyProvider" />
+ <!-- The max scale for the wallpaper when it's zoomed in -->
+ <java-symbol type="dimen" name="config_wallpaperMaxScale"/>
</resources>
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 5d42915..4b42f4ae 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -268,7 +268,7 @@
File snd_stat = new File (root_filepath + "tcp_snd");
int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat);
NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
- stats.addEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
+ stats.insertEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
NetworkStats.TAG_NONE, rx, 0, tx, 0, 0);
return stats;
}
diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
index 239f971..3ebe103 100644
--- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
+++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
@@ -54,7 +54,7 @@
recycle.txBytes = 150000;
recycle.txPackets = 1500;
recycle.operations = 0;
- mNetworkStats.addEntry(recycle);
+ mNetworkStats.insertEntry(recycle);
if (recycle.set == 1) {
uid++;
}
@@ -70,7 +70,7 @@
recycle.txBytes = 180000 * mSize;
recycle.txPackets = 1200 * mSize;
recycle.operations = 0;
- mNetworkStats.addEntry(recycle);
+ mNetworkStats.insertEntry(recycle);
}
}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index c328d72..90d8bab 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -40,7 +40,10 @@
import android.app.servertransaction.StopActivityItem;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
import android.os.IBinder;
+import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
import android.view.Display;
import android.view.View;
@@ -307,6 +310,58 @@
}
@Test
+ public void testHandleConfigurationChangedDoesntOverrideActivityConfig() {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ final Configuration oldActivityConfig =
+ new Configuration(activity.getResources().getConfiguration());
+ final DisplayMetrics oldActivityMetrics = new DisplayMetrics();
+ activity.getDisplay().getMetrics(oldActivityMetrics);
+ final Resources oldAppResources = activity.getApplication().getResources();
+ final Configuration oldAppConfig =
+ new Configuration(oldAppResources.getConfiguration());
+ final DisplayMetrics oldApplicationMetrics = new DisplayMetrics();
+ oldApplicationMetrics.setTo(oldAppResources.getDisplayMetrics());
+ assertEquals("Process config must match the top activity config by default",
+ 0, oldActivityConfig.diffPublicOnly(oldAppConfig));
+ assertEquals("Process config must match the top activity config by default",
+ oldActivityMetrics, oldApplicationMetrics);
+
+ // Update the application configuration separately from activity config
+ final Configuration newAppConfig = new Configuration(oldAppConfig);
+ newAppConfig.densityDpi += 100;
+ newAppConfig.screenHeightDp += 100;
+ final Rect newBounds = new Rect(newAppConfig.windowConfiguration.getAppBounds());
+ newBounds.bottom += 100;
+ newAppConfig.windowConfiguration.setAppBounds(newBounds);
+ newAppConfig.windowConfiguration.setBounds(newBounds);
+ newAppConfig.seq++;
+
+ final ActivityThread activityThread = activity.getActivityThread();
+ activityThread.handleConfigurationChanged(newAppConfig);
+
+ // Verify that application config update was applied, but didn't change activity config.
+ assertEquals("Activity config must not change if the process config changes",
+ oldActivityConfig, activity.getResources().getConfiguration());
+
+ final DisplayMetrics newActivityMetrics = new DisplayMetrics();
+ activity.getDisplay().getMetrics(newActivityMetrics);
+ assertEquals("Activity display size must not change if the process config changes",
+ oldActivityMetrics, newActivityMetrics);
+ final Resources newAppResources = activity.getApplication().getResources();
+ assertEquals("Application config must be updated",
+ newAppConfig, newAppResources.getConfiguration());
+ final DisplayMetrics newApplicationMetrics = new DisplayMetrics();
+ newApplicationMetrics.setTo(newAppResources.getDisplayMetrics());
+ assertNotEquals("Application display size must be updated after config update",
+ oldApplicationMetrics, newApplicationMetrics);
+ assertNotEquals("Application display size must be updated after config update",
+ newActivityMetrics, newApplicationMetrics);
+ });
+ }
+
+ @Test
public void testResumeAfterNewIntent() {
final Activity activity = mActivityTestRule.launchActivity(new Intent());
final ActivityThread activityThread = activity.getActivityThread();
diff --git a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
index 44f6407..37b2817 100644
--- a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
+++ b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
@@ -37,6 +37,8 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -129,6 +131,48 @@
assertNull(result.getCertificate());
}
+ @Test
+ public void testSourceStamp_multiApk_validStamps() throws Exception {
+ File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ File testApk2 = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ ZipFile apkZipFile = new ZipFile(testApk1);
+ ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256");
+ int size = (int) stampCertZipEntry.getSize();
+ byte[] expectedStampCertHash = new byte[size];
+ try (InputStream inputStream = apkZipFile.getInputStream(stampCertZipEntry)) {
+ inputStream.read(expectedStampCertHash);
+ }
+ List<String> apkFiles = new ArrayList<>();
+ apkFiles.add(testApk1.getAbsolutePath());
+ apkFiles.add(testApk2.getAbsolutePath());
+
+ SourceStampVerificationResult result =
+ SourceStampVerifier.verify(apkFiles);
+
+ assertTrue(result.isPresent());
+ assertTrue(result.isVerified());
+ assertNotNull(result.getCertificate());
+ byte[] actualStampCertHash =
+ MessageDigest.getInstance("SHA-256").digest(result.getCertificate().getEncoded());
+ assertArrayEquals(expectedStampCertHash, actualStampCertHash);
+ }
+
+ @Test
+ public void testSourceStamp_multiApk_invalidStamps() throws Exception {
+ File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ File testApk2 = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch.apk");
+ List<String> apkFiles = new ArrayList<>();
+ apkFiles.add(testApk1.getAbsolutePath());
+ apkFiles.add(testApk2.getAbsolutePath());
+
+ SourceStampVerificationResult result =
+ SourceStampVerifier.verify(apkFiles);
+
+ assertTrue(result.isPresent());
+ assertFalse(result.isVerified());
+ assertNull(result.getCertificate());
+ }
+
private File getApk(String apkPath) throws IOException {
File testApk = File.createTempFile("SourceStampApk", ".apk");
try (InputStream inputStream = mContext.getAssets().open(apkPath)) {
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 50cd5a3..22b4e45 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -123,7 +123,7 @@
mController = new InsetsAnimationControlImpl(controls,
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
mMockController, 10 /* durationMs */, new LinearInterpolator(),
- false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN, 0 /* animationType */);
+ false /* fade */, 0 /* animationType */);
}
@Test
@@ -182,13 +182,13 @@
@Test
public void testCancelled() {
- mController.onCancelled();
+ mController.cancel();
try {
mController.setInsetsAndAlpha(Insets.NONE, 1f /*alpha */, 0f /* fraction */);
fail("Expected exception to be thrown");
} catch (IllegalStateException ignored) {
}
- verify(mMockListener).onCancelled();
+ verify(mMockListener).onCancelled(mController);
mController.finish(true /* shown */);
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 023fc17..90a62e7 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -31,6 +31,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -47,6 +48,7 @@
import android.graphics.Rect;
import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;
+import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
@@ -56,12 +58,12 @@
import android.view.test.InsetsModeSession;
import android.widget.TextView;
-import com.android.server.testutils.OffsettableClock;
-import com.android.server.testutils.TestHandler;
-
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -72,7 +74,6 @@
import org.mockito.Mockito;
import java.util.concurrent.CountDownLatch;
-import java.util.function.Supplier;
/**
* Tests for {@link InsetsController}.
@@ -169,11 +170,8 @@
@Test
public void testControlsChanged() {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
- mController.onControlsChanged(new InsetsSourceControl[] { control });
- assertEquals(mLeash,
- mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
+ assertNotNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
mController.addOnControllableInsetsChangedListener(
((controller, typeMask) -> assertEquals(statusBars(), typeMask)));
}
@@ -183,9 +181,7 @@
OnControllableInsetsChangedListener listener
= mock(OnControllableInsetsChangedListener.class);
mController.addOnControllableInsetsChangedListener(listener);
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
- mController.onControlsChanged(new InsetsSourceControl[] { control });
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
mController.onControlsChanged(new InsetsSourceControl[0]);
assertNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl());
InOrder inOrder = Mockito.inOrder(listener);
@@ -197,20 +193,18 @@
@Test
public void testControlsRevoked_duringAnim() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
- mController.onControlsChanged(new InsetsSourceControl[] { control });
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */,
- new LinearInterpolator(), mockListener);
+ new LinearInterpolator(), new CancellationSignal(), mockListener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
verify(mockListener).onReady(any(), anyInt());
mController.onControlsChanged(new InsetsSourceControl[0]);
- verify(mockListener).onCancelled();
+ verify(mockListener).onCancelled(notNull());
});
}
@@ -224,10 +218,10 @@
WindowInsetsAnimationControlListener controlListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(),
- controlListener);
+ new CancellationSignal(), controlListener);
mController.addOnControllableInsetsChangedListener(
(controller, typeMask) -> assertEquals(0, typeMask));
- verify(controlListener).onCancelled();
+ verify(controlListener).onCancelled(null);
verify(controlListener, never()).onReady(any(), anyInt());
}
@@ -262,11 +256,8 @@
@Test
public void testApplyImeVisibility() {
- final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
-
- InsetsSourceControl[] controls = new InsetsSourceControl[3];
- controls[0] = ime;
- mController.onControlsChanged(controls);
+ InsetsSourceControl ime = createControl(ITYPE_IME);
+ mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
mController.applyImeVisibility(true);
@@ -429,9 +420,7 @@
@Test
public void testRestoreStartsAnimation() {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
- mController.onControlsChanged(new InsetsSourceControl[]{control});
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.hide(Type.statusBars());
@@ -448,7 +437,7 @@
assertTrue(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
// Gaining control
- mController.onControlsChanged(new InsetsSourceControl[]{control});
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
@@ -459,8 +448,6 @@
@Test
public void testStartImeAnimationAfterGettingControl() {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -471,7 +458,7 @@
mController.show(ime(), true /* fromIme */);
// Gaining control shortly after
- mController.onControlsChanged(new InsetsSourceControl[]{control});
+ mController.onControlsChanged(createSingletonControl(ITYPE_IME));
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
mController.cancelExistingAnimation();
@@ -483,16 +470,13 @@
@Test
public void testStartImeAnimationAfterGettingControl_imeLater() {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
-
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.show(ime());
assertFalse(mController.getState().getSource(ITYPE_IME).isVisible());
// Gaining control shortly after
- mController.onControlsChanged(new InsetsSourceControl[]{control});
+ mController.onControlsChanged(createSingletonControl(ITYPE_IME));
// Pretend IME is calling
mController.show(ime(), true /* fromIme */);
@@ -507,15 +491,13 @@
@Test
public void testAnimationEndState_controller() throws Exception {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
- mController.onControlsChanged(new InsetsSourceControl[] { control });
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(statusBars(), 0 /* durationMs */,
- new LinearInterpolator(), mockListener);
+ new LinearInterpolator(), new CancellationSignal(), mockListener);
ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
@@ -535,16 +517,15 @@
@Test
public void testCancellation_afterGainingControl() throws Exception {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
- mController.onControlsChanged(new InsetsSourceControl[] { control });
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
- CancellationSignal cancellationSignal = mController.controlWindowInsetsAnimation(
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mController.controlWindowInsetsAnimation(
statusBars(), 0 /* durationMs */,
- new LinearInterpolator(), mockListener);
+ new LinearInterpolator(), cancellationSignal, mockListener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -552,7 +533,7 @@
verify(mockListener).onReady(any(), anyInt());
cancellationSignal.cancel();
- verify(mockListener).onCancelled();
+ verify(mockListener).onCancelled(notNull());
});
waitUntilNextFrame();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -567,7 +548,8 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+ null /* cancellationSignal */, listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -591,7 +573,8 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+ null /* cancellationSignal */, listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -601,7 +584,7 @@
// Pretend that we are losing control
mController.onControlsChanged(new InsetsSourceControl[0]);
- verify(listener).onCancelled();
+ verify(listener).onCancelled(null);
});
}
@@ -611,7 +594,8 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+ null /* cancellationSignal */, listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -622,7 +606,7 @@
mTestClock.fastForward(2500);
mTestHandler.timeAdvance();
- verify(listener).onCancelled();
+ verify(listener).onCancelled(null);
});
}
@@ -632,10 +616,12 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener)
- .cancel();
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+ cancellationSignal, listener);
+ cancellationSignal.cancel();
- verify(listener).onCancelled();
+ verify(listener).onCancelled(null);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -655,12 +641,23 @@
latch.await();
}
+ private InsetsSourceControl createControl(@InternalInsetsType int type) {
+
+ // Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
+ // attempt to release mLeash directly.
+ SurfaceControl copy = new SurfaceControl();
+ copy.copyFrom(mLeash);
+ return new InsetsSourceControl(type, copy, new Point());
+ }
+
+ private InsetsSourceControl[] createSingletonControl(@InternalInsetsType int type) {
+ return new InsetsSourceControl[] { createControl(type) };
+ }
+
private InsetsSourceControl[] prepareControls() {
- final InsetsSourceControl navBar = new InsetsSourceControl(ITYPE_NAVIGATION_BAR, mLeash,
- new Point());
- final InsetsSourceControl statusBar = new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash,
- new Point());
- final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
+ final InsetsSourceControl navBar = createControl(ITYPE_NAVIGATION_BAR);
+ final InsetsSourceControl statusBar = createControl(ITYPE_STATUS_BAR);
+ final InsetsSourceControl ime = createControl(ITYPE_IME);
InsetsSourceControl[] controls = new InsetsSourceControl[3];
controls[0] = navBar;
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 9797178..03c8b1b 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -20,8 +20,9 @@
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -35,12 +36,12 @@
import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import androidx.test.runner.AndroidJUnit4;
-
/**
* Tests for {@link PendingInsetsControllerTest}.
*
@@ -95,10 +96,11 @@
public void testControl() {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- CancellationSignal signal = mPendingInsetsController.controlWindowInsetsAnimation(
- systemBars(), 0, new LinearInterpolator(), listener);
- verify(listener).onCancelled();
- assertTrue(signal.isCanceled());
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mPendingInsetsController.controlWindowInsetsAnimation(
+ systemBars(), 0, new LinearInterpolator(), cancellationSignal, listener);
+ verify(listener).onCancelled(null);
+ assertFalse(cancellationSignal.isCanceled());
}
@Test
@@ -106,10 +108,11 @@
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
mPendingInsetsController.replayAndAttach(mReplayedController);
- mPendingInsetsController.controlWindowInsetsAnimation(
- systemBars(), 0L, new LinearInterpolator(), listener);
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mPendingInsetsController.controlWindowInsetsAnimation(systemBars(), 0L,
+ new LinearInterpolator(), cancellationSignal, listener);
verify(mReplayedController).controlWindowInsetsAnimation(eq(systemBars()), eq(0L), any(),
- eq(listener));
+ eq(cancellationSignal), eq(listener));
}
@Test
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 02a88fc..5ea0718 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -20,6 +20,7 @@
import static org.testng.Assert.assertThrows;
+import android.graphics.Insets;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
@@ -172,6 +173,11 @@
}
@Override
+ void internalNotifyViewInsetsChanged(Insets viewInsets) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
public void updateContentCaptureContext(ContentCaptureContext context) {
throw new UnsupportedOperationException("should not have been called");
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java
deleted file mode 100644
index 8744997..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Locale;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ActionsModelParamsSupplierTest {
-
- @Test
- public void getSerializedPreconditions_validActionsModelParams() {
- ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
- new File("/model/file"),
- 200 /* version */,
- Collections.singletonList(Locale.forLanguageTag("en")),
- "en",
- false);
- byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
- ActionsModelParamsSupplier.ActionsModelParams params =
- new ActionsModelParamsSupplier.ActionsModelParams(
- 200 /* version */,
- "en",
- serializedPreconditions);
-
- byte[] actual = params.getSerializedPreconditions(modelFile);
-
- assertThat(actual).isEqualTo(serializedPreconditions);
- }
-
- @Test
- public void getSerializedPreconditions_invalidVersion() {
- ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
- new File("/model/file"),
- 201 /* version */,
- Collections.singletonList(Locale.forLanguageTag("en")),
- "en",
- false);
- byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
- ActionsModelParamsSupplier.ActionsModelParams params =
- new ActionsModelParamsSupplier.ActionsModelParams(
- 200 /* version */,
- "en",
- serializedPreconditions);
-
- byte[] actual = params.getSerializedPreconditions(modelFile);
-
- assertThat(actual).isNull();
- }
-
- @Test
- public void getSerializedPreconditions_invalidLocales() {
- final String LANGUAGE_TAG = "zh";
- ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
- new File("/model/file"),
- 200 /* version */,
- Collections.singletonList(Locale.forLanguageTag(LANGUAGE_TAG)),
- LANGUAGE_TAG,
- false);
- byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
- ActionsModelParamsSupplier.ActionsModelParams params =
- new ActionsModelParamsSupplier.ActionsModelParams(
- 200 /* version */,
- "en",
- serializedPreconditions);
-
- byte[] actual = params.getSerializedPreconditions(modelFile);
-
- assertThat(actual).isNull();
- }
-
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
deleted file mode 100644
index ec7e83f..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_OTHERS;
-import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_SELF;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Function;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ActionsSuggestionsHelperTest {
- private static final String LOCALE_TAG = Locale.US.toLanguageTag();
- private static final Function<CharSequence, String> LANGUAGE_DETECTOR =
- charSequence -> LOCALE_TAG;
-
- @Test
- public void testToNativeMessages_emptyInput() {
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Collections.emptyList(), LANGUAGE_DETECTOR);
-
- assertThat(conversationMessages).isEmpty();
- }
-
- @Test
- public void testToNativeMessages_noTextMessages() {
- ConversationActions.Message messageWithoutText =
- new ConversationActions.Message.Builder(PERSON_USER_OTHERS).build();
-
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Collections.singletonList(messageWithoutText), LANGUAGE_DETECTOR);
-
- assertThat(conversationMessages).isEmpty();
- }
-
- @Test
- public void testToNativeMessages_userIdEncoding() {
- Person userA = new Person.Builder().setName("userA").build();
- Person userB = new Person.Builder().setName("userB").build();
-
- ConversationActions.Message firstMessage =
- new ConversationActions.Message.Builder(userB)
- .setText("first")
- .build();
- ConversationActions.Message secondMessage =
- new ConversationActions.Message.Builder(userA)
- .setText("second")
- .build();
- ConversationActions.Message thirdMessage =
- new ConversationActions.Message.Builder(PERSON_USER_SELF)
- .setText("third")
- .build();
- ConversationActions.Message fourthMessage =
- new ConversationActions.Message.Builder(userA)
- .setText("fourth")
- .build();
-
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage),
- LANGUAGE_DETECTOR);
-
- assertThat(conversationMessages).hasLength(4);
- assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2, 0);
- assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
- assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0, 0);
- assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1, 0);
- }
-
- @Test
- public void testToNativeMessages_referenceTime() {
- ConversationActions.Message firstMessage =
- new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
- .setText("first")
- .setReferenceTime(createZonedDateTimeFromMsUtc(1000))
- .build();
- ConversationActions.Message secondMessage =
- new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
- .setText("second")
- .build();
- ConversationActions.Message thirdMessage =
- new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
- .setText("third")
- .setReferenceTime(createZonedDateTimeFromMsUtc(2000))
- .build();
-
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Arrays.asList(firstMessage, secondMessage, thirdMessage),
- LANGUAGE_DETECTOR);
-
- assertThat(conversationMessages).hasLength(3);
- assertNativeMessage(conversationMessages[0], firstMessage.getText(), 1, 1000);
- assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
- assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 1, 2000);
- }
-
- @Test
- public void testDeduplicateActions() {
- Bundle phoneExtras = new Bundle();
- Intent phoneIntent = new Intent();
- phoneIntent.setComponent(new ComponentName("phone", "intent"));
- ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
-
- Bundle anotherPhoneExtras = new Bundle();
- Intent anotherPhoneIntent = new Intent();
- anotherPhoneIntent.setComponent(new ComponentName("phone", "another.intent"));
- ExtrasUtils.putActionIntent(anotherPhoneExtras, anotherPhoneIntent);
-
- Bundle urlExtras = new Bundle();
- Intent urlIntent = new Intent();
- urlIntent.setComponent(new ComponentName("url", "intent"));
- ExtrasUtils.putActionIntent(urlExtras, urlIntent);
-
- PendingIntent pendingIntent = PendingIntent.getActivity(
- InstrumentationRegistry.getTargetContext(),
- 0,
- phoneIntent,
- 0);
- Icon icon = Icon.createWithData(new byte[0], 0, 0);
- ConversationAction action =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
- .setExtras(phoneExtras)
- .build();
- ConversationAction actionWithSameLabel =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(
- icon, "label", "2", pendingIntent))
- .setExtras(phoneExtras)
- .build();
- ConversationAction actionWithSamePackageButDifferentClass =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(
- icon, "label", "3", pendingIntent))
- .setExtras(anotherPhoneExtras)
- .build();
- ConversationAction actionWithDifferentLabel =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(
- icon, "another_label", "4", pendingIntent))
- .setExtras(phoneExtras)
- .build();
- ConversationAction actionWithDifferentPackage =
- new ConversationAction.Builder(ConversationAction.TYPE_OPEN_URL)
- .setAction(new RemoteAction(icon, "label", "5", pendingIntent))
- .setExtras(urlExtras)
- .build();
- ConversationAction actionWithoutRemoteAction =
- new ConversationAction.Builder(ConversationAction.TYPE_CREATE_REMINDER)
- .build();
-
- List<ConversationAction> conversationActions =
- ActionsSuggestionsHelper.removeActionsWithDuplicates(
- Arrays.asList(action, actionWithSameLabel,
- actionWithSamePackageButDifferentClass, actionWithDifferentLabel,
- actionWithDifferentPackage, actionWithoutRemoteAction));
-
- assertThat(conversationActions).hasSize(3);
- assertThat(conversationActions.get(0).getAction().getContentDescription()).isEqualTo("4");
- assertThat(conversationActions.get(1).getAction().getContentDescription()).isEqualTo("5");
- assertThat(conversationActions.get(2).getAction()).isNull();
- }
-
- @Test
- public void testDeduplicateActions_nullComponent() {
- Bundle phoneExtras = new Bundle();
- Intent phoneIntent = new Intent(Intent.ACTION_DIAL);
- ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
- PendingIntent pendingIntent = PendingIntent.getActivity(
- InstrumentationRegistry.getTargetContext(),
- 0,
- phoneIntent,
- 0);
- Icon icon = Icon.createWithData(new byte[0], 0, 0);
- ConversationAction action =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
- .setExtras(phoneExtras)
- .build();
- ConversationAction actionWithSameLabel =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(
- icon, "label", "2", pendingIntent))
- .setExtras(phoneExtras)
- .build();
-
- List<ConversationAction> conversationActions =
- ActionsSuggestionsHelper.removeActionsWithDuplicates(
- Arrays.asList(action, actionWithSameLabel));
-
- assertThat(conversationActions).isEmpty();
- }
-
- @Test
- public void createLabeledIntentResult_null() {
- ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
- new ActionsSuggestionsModel.ActionSuggestion(
- "text",
- ConversationAction.TYPE_OPEN_URL,
- 1.0f,
- null,
- null,
- null
- );
-
- LabeledIntent.Result labeledIntentResult =
- ActionsSuggestionsHelper.createLabeledIntentResult(
- InstrumentationRegistry.getTargetContext(),
- new TemplateIntentFactory(),
- nativeSuggestion);
-
- assertThat(labeledIntentResult).isNull();
- }
-
- @Test
- public void createLabeledIntentResult_emptyList() {
- ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
- new ActionsSuggestionsModel.ActionSuggestion(
- "text",
- ConversationAction.TYPE_OPEN_URL,
- 1.0f,
- null,
- null,
- new RemoteActionTemplate[0]
- );
-
- LabeledIntent.Result labeledIntentResult =
- ActionsSuggestionsHelper.createLabeledIntentResult(
- InstrumentationRegistry.getTargetContext(),
- new TemplateIntentFactory(),
- nativeSuggestion);
-
- assertThat(labeledIntentResult).isNull();
- }
-
- @Test
- public void createLabeledIntentResult() {
- ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
- new ActionsSuggestionsModel.ActionSuggestion(
- "text",
- ConversationAction.TYPE_OPEN_URL,
- 1.0f,
- null,
- null,
- new RemoteActionTemplate[]{
- new RemoteActionTemplate(
- "title",
- null,
- "description",
- null,
- Intent.ACTION_VIEW,
- Uri.parse("http://www.android.com").toString(),
- null,
- 0,
- null,
- null,
- null,
- 0)});
-
- LabeledIntent.Result labeledIntentResult =
- ActionsSuggestionsHelper.createLabeledIntentResult(
- InstrumentationRegistry.getTargetContext(),
- new TemplateIntentFactory(),
- nativeSuggestion);
-
- assertThat(labeledIntentResult.remoteAction.getTitle()).isEqualTo("title");
- assertThat(labeledIntentResult.resolvedIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
- }
-
- private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
- return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneId.of("UTC"));
- }
-
- private static void assertNativeMessage(
- ActionsSuggestionsModel.ConversationMessage nativeMessage,
- CharSequence text,
- int userId,
- long referenceTimeInMsUtc) {
- assertThat(nativeMessage.getText()).isEqualTo(text.toString());
- assertThat(nativeMessage.getUserId()).isEqualTo(userId);
- assertThat(nativeMessage.getDetectedTextLanguageTags()).isEqualTo(LOCALE_TAG);
- assertThat(nativeMessage.getReferenceTimeMsUtc()).isEqualTo(referenceTimeInMsUtc);
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
deleted file mode 100644
index 79e1406..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.os.LocaleList;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ModelFileManagerTest {
- private static final Locale DEFAULT_LOCALE = Locale.forLanguageTag("en-US");
- @Mock
- private Supplier<List<ModelFileManager.ModelFile>> mModelFileSupplier;
- private ModelFileManager.ModelFileSupplierImpl mModelFileSupplierImpl;
- private ModelFileManager mModelFileManager;
- private File mRootTestDir;
- private File mFactoryModelDir;
- private File mUpdatedModelFile;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mModelFileManager = new ModelFileManager(mModelFileSupplier);
- mRootTestDir = InstrumentationRegistry.getContext().getCacheDir();
- mFactoryModelDir = new File(mRootTestDir, "factory");
- mUpdatedModelFile = new File(mRootTestDir, "updated.model");
-
- mModelFileSupplierImpl =
- new ModelFileManager.ModelFileSupplierImpl(
- mFactoryModelDir,
- "test\\d.model",
- mUpdatedModelFile,
- fd -> 1,
- fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT
- );
-
- mRootTestDir.mkdirs();
- mFactoryModelDir.mkdirs();
-
- Locale.setDefault(DEFAULT_LOCALE);
- }
-
- @After
- public void removeTestDir() {
- recursiveDelete(mRootTestDir);
- }
-
- @Test
- public void get() {
- ModelFileManager.ModelFile modelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1, Collections.emptyList(), "", true);
- when(mModelFileSupplier.get()).thenReturn(Collections.singletonList(modelFile));
-
- List<ModelFileManager.ModelFile> modelFiles = mModelFileManager.listModelFiles();
-
- assertThat(modelFiles).hasSize(1);
- assertThat(modelFiles.get(0)).isEqualTo(modelFile);
- }
-
- @Test
- public void findBestModel_versionCode() {
- ModelFileManager.ModelFile olderModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.emptyList(), "", true);
-
- ModelFileManager.ModelFile newerModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 2,
- Collections.emptyList(), "", true);
- when(mModelFileSupplier.get())
- .thenReturn(Arrays.asList(olderModelFile, newerModelFile));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(LocaleList.getEmptyLocaleList());
-
- assertThat(bestModelFile).isEqualTo(newerModelFile);
- }
-
- @Test
- public void findBestModel_languageDependentModelIsPreferred() {
- Locale locale = Locale.forLanguageTag("ja");
- ModelFileManager.ModelFile languageIndependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.emptyList(), "", true);
-
- ModelFileManager.ModelFile languageDependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(locale), locale.toLanguageTag(), false);
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags(locale.toLanguageTag()));
- assertThat(bestModelFile).isEqualTo(languageDependentModelFile);
- }
-
- @Test
- public void findBestModel_noMatchedLanguageModel() {
- Locale locale = Locale.forLanguageTag("ja");
- ModelFileManager.ModelFile languageIndependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.emptyList(), "", true);
-
- ModelFileManager.ModelFile languageDependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(locale), locale.toLanguageTag(), false);
-
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags("zh-hk"));
- assertThat(bestModelFile).isEqualTo(languageIndependentModelFile);
- }
-
- @Test
- public void findBestModel_noMatchedLanguageModel_defaultLocaleModelExists() {
- ModelFileManager.ModelFile languageIndependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.emptyList(), "", true);
-
- ModelFileManager.ModelFile languageDependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(
- DEFAULT_LOCALE), DEFAULT_LOCALE.toLanguageTag(), false);
-
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags("zh-hk"));
- assertThat(bestModelFile).isEqualTo(languageIndependentModelFile);
- }
-
- @Test
- public void findBestModel_languageIsMoreImportantThanVersion() {
- ModelFileManager.ModelFile matchButOlderModel =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("fr")), "fr", false);
-
- ModelFileManager.ModelFile mismatchButNewerModel =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 2,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(matchButOlderModel, mismatchButNewerModel));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags("fr"));
- assertThat(bestModelFile).isEqualTo(matchButOlderModel);
- }
-
- @Test
- public void findBestModel_languageIsMoreImportantThanVersion_bestModelComesFirst() {
- ModelFileManager.ModelFile matchLocaleModel =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile languageIndependentModel =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 2,
- Collections.emptyList(), "", true);
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(matchLocaleModel, languageIndependentModel));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags("ja"));
-
- assertThat(bestModelFile).isEqualTo(matchLocaleModel);
- }
-
- @Test
- public void modelFileEquals() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile modelB =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- assertThat(modelA).isEqualTo(modelB);
- }
-
- @Test
- public void modelFile_different() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile modelB =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- assertThat(modelA).isNotEqualTo(modelB);
- }
-
-
- @Test
- public void modelFile_getPath() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- assertThat(modelA.getPath()).isEqualTo("/path/a");
- }
-
- @Test
- public void modelFile_getName() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- assertThat(modelA.getName()).isEqualTo("a");
- }
-
- @Test
- public void modelFile_isPreferredTo_languageDependentIsBetter() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile modelB =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 2,
- Collections.emptyList(), "", true);
-
- assertThat(modelA.isPreferredTo(modelB)).isTrue();
- }
-
- @Test
- public void modelFile_isPreferredTo_version() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 2,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile modelB =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.emptyList(), "", false);
-
- assertThat(modelA.isPreferredTo(modelB)).isTrue();
- }
-
- @Test
- public void testFileSupplierImpl_updatedFileOnly() throws IOException {
- mUpdatedModelFile.createNewFile();
- File model1 = new File(mFactoryModelDir, "test1.model");
- model1.createNewFile();
- File model2 = new File(mFactoryModelDir, "test2.model");
- model2.createNewFile();
- new File(mFactoryModelDir, "not_match_regex.model").createNewFile();
-
- List<ModelFileManager.ModelFile> modelFiles = mModelFileSupplierImpl.get();
- List<String> modelFilePaths =
- modelFiles
- .stream()
- .map(modelFile -> modelFile.getPath())
- .collect(Collectors.toList());
-
- assertThat(modelFiles).hasSize(3);
- assertThat(modelFilePaths).containsExactly(
- mUpdatedModelFile.getAbsolutePath(),
- model1.getAbsolutePath(),
- model2.getAbsolutePath());
- }
-
- @Test
- public void testFileSupplierImpl_empty() {
- mFactoryModelDir.delete();
- List<ModelFileManager.ModelFile> modelFiles = mModelFileSupplierImpl.get();
-
- assertThat(modelFiles).hasSize(0);
- }
-
- private static void recursiveDelete(File f) {
- if (f.isDirectory()) {
- for (File innerFile : f.listFiles()) {
- recursiveDelete(innerFile);
- }
- }
- f.delete();
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java
new file mode 100644
index 0000000..e4cfc53
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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 android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemTextClassifierMetadataTest {
+
+ @Test
+ public void testInvalidPackageNameThrowsException() {
+ assertThrows(NullPointerException.class,
+ () -> new SystemTextClassifierMetadata(/* packageName= */ null, /* userId= */
+ 1, /* useDefaultTextClassifier= */ false));
+ }
+
+ @Test
+ public void testParcel() {
+ SystemTextClassifierMetadata sysTcMetadata = new SystemTextClassifierMetadata(
+ "package", /* userId= */ 1, /* useDefaultTextClassifier= */ false);
+
+ Parcel p = Parcel.obtain();
+ sysTcMetadata.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ SystemTextClassifierMetadata targetSysTcMetadata =
+ SystemTextClassifierMetadata.CREATOR.createFromParcel(p);
+
+ assertThat(targetSysTcMetadata.getUserId()).isEqualTo(sysTcMetadata.getUserId());
+ assertThat(targetSysTcMetadata.getCallingPackageName()).isEqualTo(
+ sysTcMetadata.getCallingPackageName());
+ assertThat(targetSysTcMetadata.useDefaultTextClassifier()).isEqualTo(
+ sysTcMetadata.useDefaultTextClassifier());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 82fa73f..2f21b7f 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -16,7 +16,6 @@
package android.view.textclassifier;
-import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.provider.DeviceConfig;
@@ -24,8 +23,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.google.common.primitives.Floats;
-
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -57,17 +54,17 @@
public void testLoadFromDeviceConfig_IntValue() throws Exception {
// Saves config original value.
final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH);
+ TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH);
final TextClassificationConstants constants = new TextClassificationConstants();
try {
// Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH, "8");
- assertWithMessage(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH)
- .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(8);
+ setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH, "8");
+ assertWithMessage(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH)
+ .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(8);
} finally {
// Restores config original value.
- setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+ setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH,
originalValue);
}
}
@@ -94,61 +91,6 @@
}
}
- @Test
- public void testLoadFromDeviceConfig_FloatValue() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, "2");
- assertWithMessage(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE)
- .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, originalValue);
- }
- }
-
- @Test
- public void testLoadFromDeviceConfig_StringList() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.ENTITY_LIST_DEFAULT);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, "email:url");
- assertWithMessage(TextClassificationConstants.ENTITY_LIST_DEFAULT)
- .that(constants.getEntityListDefault())
- .containsExactly("email", "url");
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, originalValue);
- }
- }
-
- @Test
- public void testLoadFromDeviceConfig_FloatList() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, "30:0.5:0.3");
- assertThat(Floats.asList(constants.getLangIdContextSettings())).containsExactly(30f,
- 0.5f, 0.3f).inOrder();
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, originalValue);
- }
- }
-
private void setDeviceConfig(String key, String value) {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key,
value, /* makeDefault */ false);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 1ca4649..628252d 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -16,19 +16,15 @@
package android.view.textclassifier;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.mock;
import android.content.Context;
-import android.content.Intent;
-import android.os.LocaleList;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
@@ -38,14 +34,12 @@
@RunWith(AndroidJUnit4.class)
public class TextClassificationManagerTest {
- private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
-
private Context mContext;
private TextClassificationManager mTcm;
@Before
public void setup() {
- mContext = InstrumentationRegistry.getTargetContext();
+ mContext = ApplicationProvider.getApplicationContext();
mTcm = mContext.getSystemService(TextClassificationManager.class);
}
@@ -53,45 +47,17 @@
public void testSetTextClassifier() {
TextClassifier classifier = mock(TextClassifier.class);
mTcm.setTextClassifier(classifier);
- assertEquals(classifier, mTcm.getTextClassifier());
+ assertThat(mTcm.getTextClassifier()).isEqualTo(classifier);
}
@Test
public void testGetLocalTextClassifier() {
- assertTrue(mTcm.getTextClassifier(TextClassifier.LOCAL) instanceof TextClassifierImpl);
+ assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL)).isSameAs(TextClassifier.NO_OP);
}
@Test
public void testGetSystemTextClassifier() {
- assertTrue(mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
- }
-
- @Test
- public void testCannotResolveIntent() {
- Context fakeContext = new FakeContextBuilder()
- .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT)
- .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null)
- .build();
-
- TextClassifier fallback = TextClassifier.NO_OP;
- TextClassifier classifier = new TextClassifierImpl(
- fakeContext, new TextClassificationConstants(), fallback);
-
- String text = "Contact me at +12122537077";
- String classifiedText = "+12122537077";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification result = classifier.classifyText(request);
- TextClassification fallbackResult = fallback.classifyText(request);
-
- // classifier should not totally fail in which case it returns a fallback result.
- // It should skip the failing intent and return a result for non-failing intents.
- assertFalse(result.getActions().isEmpty());
- assertNotSame(result, fallbackResult);
+ assertThat(mTcm.getTextClassifier(TextClassifier.SYSTEM))
+ .isInstanceOf(SystemTextClassifier.class);
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index d544029..39ededa 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -17,6 +17,7 @@
package android.view.textclassifier;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -199,7 +200,9 @@
.setReferenceTime(referenceTime)
.setExtras(BUNDLE)
.build();
- reference.setCallingPackageName(packageName);
+ final SystemTextClassifierMetadata systemTcMetadata =
+ new SystemTextClassifierMetadata(packageName, 1, false);
+ reference.setSystemTextClassifierMetadata(systemTcMetadata);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -216,5 +219,11 @@
assertEquals(referenceTime, result.getReferenceTime());
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
assertEquals(packageName, result.getCallingPackageName());
+ final SystemTextClassifierMetadata resultSystemTcMetadata =
+ result.getSystemTextClassifierMetadata();
+ assertNotNull(resultSystemTcMetadata);
+ assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName());
+ assertEquals(1, resultSystemTcMetadata.getUserId());
+ assertFalse(resultSystemTcMetadata.useDefaultTextClassifier());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
deleted file mode 100644
index 372a478..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import android.app.RemoteAction;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.LocaleList;
-import android.service.textclassifier.TextClassifierService;
-import android.text.Spannable;
-import android.text.SpannableString;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.google.common.truth.Truth;
-
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Testing {@link TextClassifierTest} APIs on local and system textclassifier.
- * <p>
- * Tests are skipped if such a textclassifier does not exist.
- */
-@SmallTest
-@RunWith(Parameterized.class)
-public class TextClassifierTest {
- private static final String LOCAL = "local";
- private static final String SESSION = "session";
- private static final String DEFAULT = "default";
-
- // TODO: Add SYSTEM, which tests TextClassifier.SYSTEM.
- @Parameterized.Parameters(name = "{0}")
- public static Iterable<Object> textClassifierTypes() {
- return Arrays.asList(LOCAL, SESSION, DEFAULT);
- }
-
- @Parameterized.Parameter
- public String mTextClassifierType;
-
- private static final TextClassificationConstants TC_CONSTANTS =
- new TextClassificationConstants();
- private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
- private static final String NO_TYPE = null;
-
- private Context mContext;
- private TextClassificationManager mTcm;
- private TextClassifier mClassifier;
-
- @Before
- public void setup() {
- mContext = InstrumentationRegistry.getTargetContext();
- mTcm = mContext.getSystemService(TextClassificationManager.class);
-
- if (mTextClassifierType.equals(LOCAL)) {
- mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL);
- } else if (mTextClassifierType.equals(SESSION)) {
- mClassifier = mTcm.createTextClassificationSession(
- new TextClassificationContext.Builder(
- "android",
- TextClassifier.WIDGET_TYPE_NOTIFICATION)
- .build(),
- mTcm.getTextClassifier(TextClassifier.LOCAL));
- } else {
- mClassifier = TextClassifierService.getDefaultTextClassifierImplementation(mContext);
- }
- }
-
- @Test
- public void testSuggestSelection() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Contact me at droid@android.com";
- String selected = "droid";
- String suggested = "droid@android.com";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- int smartStartIndex = text.indexOf(suggested);
- int smartEndIndex = smartStartIndex + suggested.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
- }
-
- @Test
- public void testSuggestSelection_url() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit http://www.android.com for more information";
- String selected = "http";
- String suggested = "http://www.android.com";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- int smartStartIndex = text.indexOf(suggested);
- int smartEndIndex = smartStartIndex + suggested.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
- }
-
- @Test
- public void testSmartSelection_withEmoji() {
- if (isTextClassifierDisabled()) return;
-
- String text = "\uD83D\uDE02 Hello.";
- String selected = "Hello";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(startIndex, endIndex, NO_TYPE));
- }
-
- @Test
- public void testClassifyText() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Contact me at droid@android.com";
- String classifiedText = "droid@android.com";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_EMAIL));
- }
-
- @Test
- public void testClassifyText_url() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit www.android.com for more information";
- String classifiedText = "www.android.com";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
- assertThat(classification, containsIntentWithAction(Intent.ACTION_VIEW));
- }
-
- @Test
- public void testClassifyText_address() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Brandschenkestrasse 110, Zürich, Switzerland";
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, 0, text.length())
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(text, TextClassifier.TYPE_ADDRESS));
- }
-
- @Test
- public void testClassifyText_url_inCaps() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit HTTP://ANDROID.COM for more information";
- String classifiedText = "HTTP://ANDROID.COM";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
- assertThat(classification, containsIntentWithAction(Intent.ACTION_VIEW));
- }
-
- @Test
- public void testClassifyText_date() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Let's meet on January 9, 2018.";
- String classifiedText = "January 9, 2018";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_DATE));
- Bundle extras = classification.getExtras();
- List<Bundle> entities = ExtrasUtils.getEntities(extras);
- Truth.assertThat(entities).hasSize(1);
- Bundle entity = entities.get(0);
- Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_DATE);
- }
-
- @Test
- public void testClassifyText_datetime() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Let's meet 2018/01/01 10:30:20.";
- String classifiedText = "2018/01/01 10:30:20";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification,
- isTextClassification(classifiedText, TextClassifier.TYPE_DATE_TIME));
- }
-
- @Test
- public void testClassifyText_foreignText() {
- LocaleList originalLocales = LocaleList.getDefault();
- LocaleList.setDefault(LocaleList.forLanguageTags("en"));
- String japaneseText = "これは日本語のテキストです";
-
- Context context = new FakeContextBuilder()
- .setIntentComponent(Intent.ACTION_TRANSLATE, FakeContextBuilder.DEFAULT_COMPONENT)
- .build();
- TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
- TextClassification.Request request = new TextClassification.Request.Builder(
- japaneseText, 0, japaneseText.length())
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = classifier.classifyText(request);
- RemoteAction translateAction = classification.getActions().get(0);
- assertEquals(1, classification.getActions().size());
- assertEquals(
- context.getString(com.android.internal.R.string.translate),
- translateAction.getTitle());
-
- assertEquals(translateAction, ExtrasUtils.findTranslateAction(classification));
- Intent intent = ExtrasUtils.getActionsIntents(classification).get(0);
- assertEquals(Intent.ACTION_TRANSLATE, intent.getAction());
- Bundle foreignLanguageInfo = ExtrasUtils.getForeignLanguageExtra(classification);
- assertEquals("ja", ExtrasUtils.getEntityType(foreignLanguageInfo));
- assertTrue(ExtrasUtils.getScore(foreignLanguageInfo) >= 0);
- assertTrue(ExtrasUtils.getScore(foreignLanguageInfo) <= 1);
- assertTrue(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER));
- assertEquals("ja", ExtrasUtils.getTopLanguage(intent).getLanguage());
-
- LocaleList.setDefault(originalLocales);
- }
-
- @Test
- public void testGenerateLinks_phone() {
- if (isTextClassifierDisabled()) return;
- String text = "The number is +12122537077. See you tonight!";
- TextLinks.Request request = new TextLinks.Request.Builder(text).build();
- assertThat(mClassifier.generateLinks(request),
- isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
- }
-
- @Test
- public void testGenerateLinks_exclude() {
- if (isTextClassifierDisabled()) return;
- String text = "You want apple@banana.com. See you tonight!";
- List<String> hints = Collections.EMPTY_LIST;
- List<String> included = Collections.EMPTY_LIST;
- List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
- }
-
- @Test
- public void testGenerateLinks_explicit_address() {
- if (isTextClassifierDisabled()) return;
- String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
- List<String> explicit = Arrays.asList(TextClassifier.TYPE_ADDRESS);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.createWithExplicitEntityList(explicit))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
- TextClassifier.TYPE_ADDRESS));
- }
-
- @Test
- public void testGenerateLinks_exclude_override() {
- if (isTextClassifierDisabled()) return;
- String text = "You want apple@banana.com. See you tonight!";
- List<String> hints = Collections.EMPTY_LIST;
- List<String> included = Arrays.asList(TextClassifier.TYPE_EMAIL);
- List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
- }
-
- @Test
- public void testGenerateLinks_maxLength() {
- if (isTextClassifierDisabled()) return;
- char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
- Arrays.fill(manySpaces, ' ');
- TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
- TextLinks links = mClassifier.generateLinks(request);
- assertTrue(links.getLinks().isEmpty());
- }
-
- @Test
- public void testApplyLinks_unsupportedCharacter() {
- if (isTextClassifierDisabled()) return;
- Spannable url = new SpannableString("\u202Emoc.diordna.com");
- TextLinks.Request request = new TextLinks.Request.Builder(url).build();
- assertEquals(
- TextLinks.STATUS_UNSUPPORTED_CHARACTER,
- mClassifier.generateLinks(request).apply(url, 0, null));
- }
-
- @Test
- public void testGenerateLinks_tooLong() {
- if (isTextClassifierDisabled()) return;
- char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
- Arrays.fill(manySpaces, ' ');
- TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
- TextLinks links = mClassifier.generateLinks(request);
- assertTrue(links.getLinks().isEmpty());
- }
-
- @Test
- public void testGenerateLinks_entityData() {
- if (isTextClassifierDisabled()) return;
- String text = "The number is +12122537077.";
- Bundle extras = new Bundle();
- ExtrasUtils.putIsSerializedEntityDataEnabled(extras, true);
- TextLinks.Request request = new TextLinks.Request.Builder(text).setExtras(extras).build();
-
- TextLinks textLinks = mClassifier.generateLinks(request);
-
- Truth.assertThat(textLinks.getLinks()).hasSize(1);
- TextLinks.TextLink textLink = textLinks.getLinks().iterator().next();
- List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras());
- Truth.assertThat(entities).hasSize(1);
- Bundle entity = entities.get(0);
- Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_PHONE);
- }
-
- @Test
- public void testGenerateLinks_entityData_disabled() {
- if (isTextClassifierDisabled()) return;
- String text = "The number is +12122537077.";
- TextLinks.Request request = new TextLinks.Request.Builder(text).build();
-
- TextLinks textLinks = mClassifier.generateLinks(request);
-
- Truth.assertThat(textLinks.getLinks()).hasSize(1);
- TextLinks.TextLink textLink = textLinks.getLinks().iterator().next();
- List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras());
- Truth.assertThat(entities).isNull();
- }
-
- @Test
- public void testDetectLanguage() {
- if (isTextClassifierDisabled()) return;
- String text = "This is English text";
- TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
- TextLanguage textLanguage = mClassifier.detectLanguage(request);
- assertThat(textLanguage, isTextLanguage("en"));
- }
-
- @Test
- public void testDetectLanguage_japanese() {
- if (isTextClassifierDisabled()) return;
- String text = "これは日本語のテキストです";
- TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
- TextLanguage textLanguage = mClassifier.detectLanguage(request);
- assertThat(textLanguage, isTextLanguage("ja"));
- }
-
- @Ignore // Doesn't work without a language-based model.
- @Test
- public void testSuggestConversationActions_textReplyOnly_maxOne() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("Where are you?")
- .build();
- TextClassifier.EntityConfig typeConfig =
- new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(1)
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
- ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
- Truth.assertThat(conversationAction.getType()).isEqualTo(
- ConversationAction.TYPE_TEXT_REPLY);
- Truth.assertThat(conversationAction.getTextReply()).isNotNull();
- }
-
- @Ignore // Doesn't work without a language-based model.
- @Test
- public void testSuggestConversationActions_textReplyOnly_noMax() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("Where are you?")
- .build();
- TextClassifier.EntityConfig typeConfig =
- new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- assertTrue(conversationActions.getConversationActions().size() > 1);
- for (ConversationAction conversationAction :
- conversationActions.getConversationActions()) {
- assertThat(conversationAction,
- isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
- }
- }
-
- @Test
- public void testSuggestConversationActions_openUrl() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("Check this out: https://www.android.com")
- .build();
- TextClassifier.EntityConfig typeConfig =
- new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_OPEN_URL))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(1)
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
- ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
- Truth.assertThat(conversationAction.getType()).isEqualTo(ConversationAction.TYPE_OPEN_URL);
- Intent actionIntent = ExtrasUtils.getActionIntent(conversationAction.getExtras());
- Truth.assertThat(actionIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
- Truth.assertThat(actionIntent.getData()).isEqualTo(Uri.parse("https://www.android.com"));
- }
-
- @Ignore // Doesn't work without a language-based model.
- @Test
- public void testSuggestConversationActions_copy() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("Authentication code: 12345")
- .build();
- TextClassifier.EntityConfig typeConfig =
- new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_COPY))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(1)
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
- ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
- Truth.assertThat(conversationAction.getType()).isEqualTo(ConversationAction.TYPE_COPY);
- Truth.assertThat(conversationAction.getTextReply()).isAnyOf(null, "");
- Truth.assertThat(conversationAction.getAction()).isNull();
- String code = ExtrasUtils.getCopyText(conversationAction.getExtras());
- Truth.assertThat(code).isEqualTo("12345");
- Truth.assertThat(
- ExtrasUtils.getSerializedEntityData(conversationAction.getExtras())).isNotEmpty();
- }
-
- @Test
- public void testSuggestConversationActions_deduplicate() {
- Context context = new FakeContextBuilder()
- .setIntentComponent(Intent.ACTION_SENDTO, FakeContextBuilder.DEFAULT_COMPONENT)
- .build();
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("a@android.com b@android.com")
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(3)
- .build();
-
- TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
- ConversationActions conversationActions = classifier.suggestConversationActions(request);
-
- Truth.assertThat(conversationActions.getConversationActions()).isEmpty();
- }
-
- private boolean isTextClassifierDisabled() {
- return mClassifier == null || mClassifier == TextClassifier.NO_OP;
- }
-
- private static Matcher<TextSelection> isTextSelection(
- final int startIndex, final int endIndex, final String type) {
- return new BaseMatcher<TextSelection>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextSelection) {
- TextSelection selection = (TextSelection) o;
- return startIndex == selection.getSelectionStartIndex()
- && endIndex == selection.getSelectionEndIndex()
- && typeMatches(selection, type);
- }
- return false;
- }
-
- private boolean typeMatches(TextSelection selection, String type) {
- return type == null
- || (selection.getEntityCount() > 0
- && type.trim().equalsIgnoreCase(selection.getEntity(0)));
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendValue(
- String.format("%d, %d, %s", startIndex, endIndex, type));
- }
- };
- }
-
- private static Matcher<TextLinks> isTextLinksContaining(
- final String text, final String substring, final String type) {
- return new BaseMatcher<TextLinks>() {
-
- @Override
- public void describeTo(Description description) {
- description.appendText("text=").appendValue(text)
- .appendText(", substring=").appendValue(substring)
- .appendText(", type=").appendValue(type);
- }
-
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextLinks) {
- for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
- if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
- return type.equals(link.getEntity(0));
- }
- }
- }
- return false;
- }
- };
- }
-
- private static Matcher<TextClassification> isTextClassification(
- final String text, final String type) {
- return new BaseMatcher<TextClassification>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextClassification) {
- TextClassification result = (TextClassification) o;
- return text.equals(result.getText())
- && result.getEntityCount() > 0
- && type.equals(result.getEntity(0));
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("text=").appendValue(text)
- .appendText(", type=").appendValue(type);
- }
- };
- }
-
- private static Matcher<TextClassification> containsIntentWithAction(final String action) {
- return new BaseMatcher<TextClassification>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextClassification) {
- TextClassification result = (TextClassification) o;
- return ExtrasUtils.findAction(result, action) != null;
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("intent action=").appendValue(action);
- }
- };
- }
-
- private static Matcher<TextLanguage> isTextLanguage(final String languageTag) {
- return new BaseMatcher<TextLanguage>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextLanguage) {
- TextLanguage result = (TextLanguage) o;
- return result.getLocaleHypothesisCount() > 0
- && languageTag.equals(result.getLocale(0).toLanguageTag());
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("locale=").appendValue(languageTag);
- }
- };
- }
-
- private static Matcher<ConversationAction> isConversationAction(String actionType) {
- return new BaseMatcher<ConversationAction>() {
- @Override
- public boolean matches(Object o) {
- if (!(o instanceof ConversationAction)) {
- return false;
- }
- ConversationAction conversationAction =
- (ConversationAction) o;
- if (!actionType.equals(conversationAction.getType())) {
- return false;
- }
- if (ConversationAction.TYPE_TEXT_REPLY.equals(actionType)) {
- if (conversationAction.getTextReply() == null) {
- return false;
- }
- }
- if (conversationAction.getConfidenceScore() < 0
- || conversationAction.getConfidenceScore() > 1) {
- return false;
- }
- return true;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("actionType=").appendValue(actionType);
- }
- };
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index d0d32e3..31f8029 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -17,6 +17,8 @@
package android.view.textclassifier;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import android.icu.util.ULocale;
import android.os.Bundle;
@@ -75,7 +77,9 @@
final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
.setExtras(bundle)
.build();
- reference.setCallingPackageName(packageName);
+ final SystemTextClassifierMetadata systemTcMetadata =
+ new SystemTextClassifierMetadata(packageName, 1, false);
+ reference.setSystemTextClassifierMetadata(systemTcMetadata);
final Parcel parcel = Parcel.obtain();
reference.writeToParcel(parcel, 0);
@@ -85,5 +89,11 @@
assertEquals(text, result.getText());
assertEquals("bundle", result.getExtras().getString(bundleKey));
assertEquals(packageName, result.getCallingPackageName());
+ final SystemTextClassifierMetadata resultSystemTcMetadata =
+ result.getSystemTextClassifierMetadata();
+ assertNotNull(resultSystemTcMetadata);
+ assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName());
+ assertEquals(1, resultSystemTcMetadata.getUserId());
+ assertFalse(resultSystemTcMetadata.useDefaultTextClassifier());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index ec5813f..4f0b44b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -17,6 +17,8 @@
package android.view.textclassifier;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import android.os.Bundle;
import android.os.LocaleList;
@@ -115,7 +117,9 @@
.setExtras(BUNDLE)
.setReferenceTime(referenceTime)
.build();
- reference.setCallingPackageName(packageName);
+ final SystemTextClassifierMetadata systemTcMetadata =
+ new SystemTextClassifierMetadata(packageName, 1, false);
+ reference.setSystemTextClassifierMetadata(systemTcMetadata);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -132,5 +136,11 @@
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
assertEquals(packageName, result.getCallingPackageName());
assertEquals(referenceTime, result.getReferenceTime());
+ final SystemTextClassifierMetadata resultSystemTcMetadata =
+ result.getSystemTextClassifierMetadata();
+ assertNotNull(resultSystemTcMetadata);
+ assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName());
+ assertEquals(1, resultSystemTcMetadata.getUserId());
+ assertFalse(resultSystemTcMetadata.useDefaultTextClassifier());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 30cc4e8..82788c8 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -17,6 +17,8 @@
package android.view.textclassifier;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import android.os.Bundle;
import android.os.LocaleList;
@@ -82,7 +84,9 @@
.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
.setExtras(BUNDLE)
.build();
- reference.setCallingPackageName(packageName);
+ final SystemTextClassifierMetadata systemTcMetadata =
+ new SystemTextClassifierMetadata(packageName, 1, false);
+ reference.setSystemTextClassifierMetadata(systemTcMetadata);
// Parcel and unparcel.
final Parcel parcel = Parcel.obtain();
@@ -96,5 +100,11 @@
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
assertEquals(packageName, result.getCallingPackageName());
+ final SystemTextClassifierMetadata resultSystemTcMetadata =
+ result.getSystemTextClassifierMetadata();
+ assertNotNull(resultSystemTcMetadata);
+ assertEquals(packageName, resultSystemTcMetadata.getCallingPackageName());
+ assertEquals(1, resultSystemTcMetadata.getUserId());
+ assertFalse(resultSystemTcMetadata.useDefaultTextClassifier());
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
deleted file mode 100644
index 3ad26f5..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.textclassifier.FakeContextBuilder;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class LabeledIntentTest {
- private static final String TITLE_WITHOUT_ENTITY = "Map";
- private static final String TITLE_WITH_ENTITY = "Map NW14D1";
- private static final String DESCRIPTION = "Check the map";
- private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open map";
- private static final Intent INTENT =
- new Intent(Intent.ACTION_VIEW).setDataAndNormalize(Uri.parse("http://www.android.com"));
- private static final int REQUEST_CODE = 42;
- private static final Bundle TEXT_LANGUAGES_BUNDLE = Bundle.EMPTY;
- private static final String APP_LABEL = "fake";
-
- private Context mContext;
-
- @Before
- public void setup() {
- final ComponentName component = FakeContextBuilder.DEFAULT_COMPONENT;
- mContext = new FakeContextBuilder()
- .setIntentComponent(Intent.ACTION_VIEW, component)
- .setAppLabel(component.getPackageName(), APP_LABEL)
- .build();
- }
-
- @Test
- public void resolve_preferTitleWithEntity() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITH_ENTITY);
- assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
- Intent intent = result.resolvedIntent;
- assertThat(intent.getAction()).isEqualTo(intent.getAction());
- assertThat(intent.getComponent()).isNotNull();
- assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
- }
-
- @Test
- public void resolve_useAvailableTitle() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY);
- assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
- Intent intent = result.resolvedIntent;
- assertThat(intent.getAction()).isEqualTo(intent.getAction());
- assertThat(intent.getComponent()).isNotNull();
- }
-
- @Test
- public void resolve_titleChooser() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, (labeledIntent1, resolveInfo) -> "chooser", TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getTitle()).isEqualTo("chooser");
- assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
- Intent intent = result.resolvedIntent;
- assertThat(intent.getAction()).isEqualTo(intent.getAction());
- assertThat(intent.getComponent()).isNotNull();
- }
-
- @Test
- public void resolve_titleChooserReturnsNull() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, (labeledIntent1, resolveInfo) -> null, TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY);
- assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
- Intent intent = result.resolvedIntent;
- assertThat(intent.getAction()).isEqualTo(intent.getAction());
- assertThat(intent.getComponent()).isNotNull();
- }
-
- @Test
- public void resolve_missingTitle() {
- assertThrows(
- IllegalArgumentException.class,
- () ->
- new LabeledIntent(
- null,
- null,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- ));
- }
-
- @Test
- public void resolve_noIntentHandler() {
- // See setup(). mContext can only resolve Intent.ACTION_VIEW.
- Intent unresolvableIntent = new Intent(Intent.ACTION_TRANSLATE);
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- unresolvableIntent,
- REQUEST_CODE);
-
- LabeledIntent.Result result = labeledIntent.resolve(mContext, null, null);
-
- assertThat(result).isNull();
- }
-
- @Test
- public void resolve_descriptionWithAppName() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getContentDescription()).isEqualTo("Use fake to open map");
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
deleted file mode 100644
index 8891d3f..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Intent;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LegacyIntentClassificationFactoryTest {
-
- private static final String TEXT = "text";
-
- private LegacyClassificationIntentFactory mLegacyIntentClassificationFactory;
-
- @Before
- public void setup() {
- mLegacyIntentClassificationFactory = new LegacyClassificationIntentFactory();
- }
-
- @Test
- public void create_typeDictionary() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_DICTIONARY,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- 0L,
- 0L,
- 0d);
-
- List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ false,
- null,
- classificationResult);
-
- assertThat(intents).hasSize(1);
- LabeledIntent labeledIntent = intents.get(0);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
- assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
- }
-
- @Test
- public void create_translateAndDictionary() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_DICTIONARY,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- 0L,
- 0L,
- 0d);
-
- List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ true,
- null,
- classificationResult);
-
- assertThat(intents).hasSize(2);
- assertThat(intents.get(0).intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
- assertThat(intents.get(1).intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
deleted file mode 100644
index bcea5fe..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.Intent;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TemplateClassificationIntentFactoryTest {
-
- private static final String TEXT = "text";
- private static final String TITLE_WITHOUT_ENTITY = "Map";
- private static final String DESCRIPTION = "Opens in Maps";
- private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open Map";
- private static final String ACTION = Intent.ACTION_VIEW;
-
- @Mock
- private ClassificationIntentFactory mFallback;
- private TemplateClassificationIntentFactory mTemplateClassificationIntentFactory;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mTemplateClassificationIntentFactory = new TemplateClassificationIntentFactory(
- new TemplateIntentFactory(),
- mFallback);
- }
-
- @Test
- public void create_foreignText() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_ADDRESS,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- createRemoteActionTemplates(),
- 0L,
- 0L,
- 0d);
-
- List<LabeledIntent> intents =
- mTemplateClassificationIntentFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ true,
- null,
- classificationResult);
-
- assertThat(intents).hasSize(2);
- LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(ACTION);
-
- labeledIntent = intents.get(1);
- intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
- }
-
- @Test
- public void create_notForeignText() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_ADDRESS,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- createRemoteActionTemplates(),
- 0L,
- 0L,
- 0d);
-
- List<LabeledIntent> intents =
- mTemplateClassificationIntentFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ false,
- null,
- classificationResult);
-
- assertThat(intents).hasSize(1);
- LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(ACTION);
- }
-
- @Test
- public void create_nullTemplate() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_ADDRESS,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- 0L,
- 0L,
- 0d);
-
- mTemplateClassificationIntentFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ false,
- null,
- classificationResult);
-
-
- verify(mFallback).create(
- same(InstrumentationRegistry.getContext()), eq(TEXT), eq(false), eq(null),
- same(classificationResult));
- }
-
- @Test
- public void create_emptyResult() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_ADDRESS,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- new RemoteActionTemplate[0],
- 0L,
- 0L,
- 0d);
-
- mTemplateClassificationIntentFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ false,
- null,
- classificationResult);
-
-
- verify(mFallback, never()).create(
- any(Context.class), eq(TEXT), eq(false), eq(null),
- any(AnnotatorModel.ClassificationResult.class));
- }
-
-
- private static RemoteActionTemplate[] createRemoteActionTemplates() {
- return new RemoteActionTemplate[]{
- new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- )
- };
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
deleted file mode 100644
index a33c358..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Intent;
-import android.net.Uri;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.NamedVariant;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TemplateIntentFactoryTest {
-
- private static final String TEXT = "text";
- private static final String TITLE_WITHOUT_ENTITY = "Map";
- private static final String TITLE_WITH_ENTITY = "Map NW14D1";
- private static final String DESCRIPTION = "Check the map";
- private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open map";
- private static final String ACTION = Intent.ACTION_VIEW;
- private static final String DATA = Uri.parse("http://www.android.com").toString();
- private static final String TYPE = "text/html";
- private static final Integer FLAG = Intent.FLAG_ACTIVITY_NEW_TASK;
- private static final String[] CATEGORY =
- new String[]{Intent.CATEGORY_DEFAULT, Intent.CATEGORY_APP_BROWSER};
- private static final String PACKAGE_NAME = "pkg.name";
- private static final String KEY_ONE = "key1";
- private static final String VALUE_ONE = "value1";
- private static final String KEY_TWO = "key2";
- private static final int VALUE_TWO = 42;
-
- private static final NamedVariant[] NAMED_VARIANTS = new NamedVariant[]{
- new NamedVariant(KEY_ONE, VALUE_ONE),
- new NamedVariant(KEY_TWO, VALUE_TWO)
- };
- private static final Integer REQUEST_CODE = 10;
-
- private TemplateIntentFactory mTemplateIntentFactory;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mTemplateIntentFactory = new TemplateIntentFactory();
- }
-
- @Test
- public void create_full() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- DATA,
- TYPE,
- FLAG,
- CATEGORY,
- /* packageName */ null,
- NAMED_VARIANTS,
- REQUEST_CODE
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
-
- assertThat(intents).hasSize(1);
- LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
- assertThat(labeledIntent.titleWithEntity).isEqualTo(TITLE_WITH_ENTITY);
- assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
- assertThat(labeledIntent.descriptionWithAppName).isEqualTo(DESCRIPTION_WITH_APP_NAME);
- assertThat(labeledIntent.requestCode).isEqualTo(REQUEST_CODE);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(ACTION);
- assertThat(intent.getData().toString()).isEqualTo(DATA);
- assertThat(intent.getType()).isEqualTo(TYPE);
- assertThat(intent.getFlags()).isEqualTo(FLAG);
- assertThat(intent.getCategories()).containsExactly((Object[]) CATEGORY);
- assertThat(intent.getPackage()).isNull();
- assertThat(intent.getStringExtra(KEY_ONE)).isEqualTo(VALUE_ONE);
- assertThat(intent.getIntExtra(KEY_TWO, 0)).isEqualTo(VALUE_TWO);
- }
-
- @Test
- public void normalizesScheme() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- "HTTp://www.android.com",
- TYPE,
- FLAG,
- CATEGORY,
- /* packageName */ null,
- NAMED_VARIANTS,
- REQUEST_CODE
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- String data = intents.get(0).intent.getData().toString();
- assertThat(data).isEqualTo("http://www.android.com");
- }
-
- @Test
- public void create_minimal() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- ACTION,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
-
- assertThat(intents).hasSize(1);
- LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
- assertThat(labeledIntent.titleWithEntity).isNull();
- assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
- assertThat(labeledIntent.requestCode).isEqualTo(
- LabeledIntent.DEFAULT_REQUEST_CODE);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(ACTION);
- assertThat(intent.getData()).isNull();
- assertThat(intent.getType()).isNull();
- assertThat(intent.getFlags()).isEqualTo(0);
- assertThat(intent.getCategories()).isNull();
- assertThat(intent.getPackage()).isNull();
- }
-
- @Test
- public void invalidTemplate_nullTemplate() {
- RemoteActionTemplate remoteActionTemplate = null;
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-
- @Test
- public void invalidTemplate_nonEmptyPackageName() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- DATA,
- TYPE,
- FLAG,
- CATEGORY,
- PACKAGE_NAME,
- NAMED_VARIANTS,
- REQUEST_CODE
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-
- @Test
- public void invalidTemplate_emptyTitle() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- null,
- null,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-
- @Test
- public void invalidTemplate_emptyDescription() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- null,
- null,
- ACTION,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-
- @Test
- public void invalidTemplate_emptyIntentAction() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
deleted file mode 100644
index 5e8e582..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-
-import android.metrics.LogMaker;
-import android.util.ArrayMap;
-import android.view.textclassifier.GenerateLinksLogger;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextLinks;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class GenerateLinksLoggerTest {
-
- private static final String PACKAGE_NAME = "packageName";
- private static final String ZERO = "0";
- private static final int LATENCY_MS = 123;
-
- @Test
- public void testLogGenerateLinks() {
- final String phoneText = "+12122537077";
- final String addressText = "1600 Amphitheater Parkway, Mountain View, CA";
- final String testText = "The number is " + phoneText + ", the address is " + addressText;
- final int phoneOffset = testText.indexOf(phoneText);
- final int addressOffset = testText.indexOf(addressText);
-
- final Map<String, Float> phoneEntityScores = new ArrayMap<>();
- phoneEntityScores.put(TextClassifier.TYPE_PHONE, 0.9f);
- phoneEntityScores.put(TextClassifier.TYPE_OTHER, 0.1f);
- final Map<String, Float> addressEntityScores = new ArrayMap<>();
- addressEntityScores.put(TextClassifier.TYPE_ADDRESS, 1f);
-
- TextLinks links = new TextLinks.Builder(testText)
- .addLink(phoneOffset, phoneOffset + phoneText.length(), phoneEntityScores)
- .addLink(addressOffset, addressOffset + addressText.length(), addressEntityScores)
- .build();
-
- // Set up mock.
- MetricsLogger metricsLogger = mock(MetricsLogger.class);
- ArgumentCaptor<LogMaker> logMakerCapture = ArgumentCaptor.forClass(LogMaker.class);
- doNothing().when(metricsLogger).write(logMakerCapture.capture());
-
- // Generate the log.
- GenerateLinksLogger logger = new GenerateLinksLogger(1 /* sampleRate */, metricsLogger);
- logger.logGenerateLinks(testText, links, PACKAGE_NAME, LATENCY_MS);
-
- // Validate.
- List<LogMaker> logs = logMakerCapture.getAllValues();
- assertEquals(3, logs.size());
- assertHasLog(logs, "" /* entityType */, 2, phoneText.length() + addressText.length(),
- testText.length());
- assertHasLog(logs, TextClassifier.TYPE_ADDRESS, 1, addressText.length(),
- testText.length());
- assertHasLog(logs, TextClassifier.TYPE_PHONE, 1, phoneText.length(),
- testText.length());
- }
-
- private void assertHasLog(List<LogMaker> logs, String entityType, int numLinks,
- int linkTextLength, int textLength) {
- for (LogMaker log : logs) {
- if (!entityType.equals(getEntityType(log))) {
- continue;
- }
- assertEquals(PACKAGE_NAME, log.getPackageName());
- assertNotNull(Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID)));
- assertEquals(numLinks, getIntValue(log, MetricsEvent.FIELD_LINKIFY_NUM_LINKS));
- assertEquals(linkTextLength, getIntValue(log, MetricsEvent.FIELD_LINKIFY_LINK_LENGTH));
- assertEquals(textLength, getIntValue(log, MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH));
- assertEquals(LATENCY_MS, getIntValue(log, MetricsEvent.FIELD_LINKIFY_LATENCY));
- return;
- }
- fail("No log for entity type \"" + entityType + "\"");
- }
-
- private static String getEntityType(LogMaker log) {
- return Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "");
- }
-
- private static int getIntValue(LogMaker log, int eventField) {
- return Integer.parseInt(Objects.toString(log.getTaggedData(eventField), ZERO));
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java
deleted file mode 100644
index 321a7f2..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.view.textclassifier.logging;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.google.common.truth.Truth;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SmartSelectionEventTrackerTest {
-
- @Test
- public void getVersionInfo_valid() {
- String signature = "a|702|b";
- String versionInfo = SmartSelectionEventTracker.SelectionEvent.getVersionInfo(signature);
- Truth.assertThat(versionInfo).isEqualTo("702");
- }
-
- @Test
- public void getVersionInfo_invalid() {
- String signature = "|702";
- String versionInfo = SmartSelectionEventTracker.SelectionEvent.getVersionInfo(signature);
- Truth.assertThat(versionInfo).isEmpty();
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
deleted file mode 100644
index 2c540e5..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.CONVERSATION_ACTIONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.metrics.LogMaker;
-import android.view.textclassifier.ConversationAction;
-import android.view.textclassifier.TextClassificationContext;
-import android.view.textclassifier.TextClassifierEvent;
-import android.view.textclassifier.TextClassifierEventTronLogger;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.logging.MetricsLogger;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TextClassifierEventTronLoggerTest {
- private static final String WIDGET_TYPE = "notification";
- private static final String PACKAGE_NAME = "pkg";
-
- @Mock
- private MetricsLogger mMetricsLogger;
- private TextClassifierEventTronLogger mTextClassifierEventTronLogger;
-
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mTextClassifierEventTronLogger = new TextClassifierEventTronLogger(mMetricsLogger);
- }
-
- @Test
- public void testWriteEvent() {
- TextClassificationContext textClassificationContext =
- new TextClassificationContext.Builder(PACKAGE_NAME, WIDGET_TYPE)
- .build();
- TextClassifierEvent.ConversationActionsEvent textClassifierEvent =
- new TextClassifierEvent.ConversationActionsEvent.Builder(
- TextClassifierEvent.TYPE_SMART_ACTION)
- .setEntityTypes(ConversationAction.TYPE_CALL_PHONE)
- .setScores(0.5f)
- .setEventContext(textClassificationContext)
- .build();
-
- mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
-
- ArgumentCaptor<LogMaker> captor = ArgumentCaptor.forClass(LogMaker.class);
- Mockito.verify(mMetricsLogger).write(captor.capture());
- LogMaker logMaker = captor.getValue();
- assertThat(logMaker.getCategory()).isEqualTo(CONVERSATION_ACTIONS);
- assertThat(logMaker.getSubtype()).isEqualTo(ACTION_TEXT_SELECTION_SMART_SHARE);
- assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE))
- .isEqualTo(ConversationAction.TYPE_CALL_PHONE);
- assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE))
- .isWithin(0.00001f).of(0.5f);
- // Never write event time.
- assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME)).isNull();
- assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
- assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE))
- .isEqualTo(WIDGET_TYPE);
-
- }
-
- @Test
- public void testWriteEvent_unsupportedCategory() {
- TextClassifierEvent.TextSelectionEvent textClassifierEvent =
- new TextClassifierEvent.TextSelectionEvent.Builder(
- TextClassifierEvent.TYPE_SMART_ACTION)
- .build();
-
- mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
-
- Mockito.verify(mMetricsLogger, Mockito.never()).write(Mockito.any(LogMaker.class));
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
index 36dd3e4..03ed68c 100644
--- a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
@@ -98,6 +98,11 @@
@Override
void handleResultMessage(Message message) {}
+
+ @Override
+ List<ComponentName> getTopComponentNames(int topK) {
+ return null;
+ }
};
return testComparator;
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 24e96d4..a6cbc1a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -987,7 +987,8 @@
/* resolveInfoPresentationGetter */ null),
serviceTargets,
TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos)
+ directShareToShortcutInfos,
+ null)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
@@ -1058,7 +1059,8 @@
/* resolveInfoPresentationGetter */ null),
serviceTargets,
TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos)
+ directShareToShortcutInfos,
+ null)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
@@ -1145,7 +1147,8 @@
/* resolveInfoPresentationGetter */ null),
serviceTargets,
TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos)
+ directShareToShortcutInfos,
+ null)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index abfb4fb..8cf146e 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -48,6 +48,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
@@ -56,6 +57,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -87,6 +89,7 @@
static {
MANAGED_PROFILE_INFO.id = 10;
MANAGED_PROFILE_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE;
+ MANAGED_PROFILE_INFO.userType = UserManager.USER_TYPE_PROFILE_MANAGED;
}
private static UserInfo CURRENT_USER_INFO = new UserInfo();
@@ -116,12 +119,21 @@
private Context mContext;
public static final String PHONE_NUMBER = "123-456-789";
+ private int mDeviceProvisionedInitialValue;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getTargetContext();
sInjector = spy(new TestInjector());
+ mDeviceProvisionedInitialValue = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, /* def= */ 0);
+ }
+
+ @After
+ public void tearDown() {
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+ mDeviceProvisionedInitialValue);
}
@Test
@@ -533,6 +545,22 @@
}
@Test
+ public void shouldSkipDisclosure_duringDeviceSetup() throws RemoteException {
+ setupShouldSkipDisclosureTest();
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+ /* value= */ 0);
+ Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.fromParts("http", "apache.org", null));
+
+ mActivityRule.launchActivity(intent);
+
+ verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyInt(), anyInt());
+ }
+
+ @Test
public void forwardToManagedProfile_LoggingTest() throws Exception {
sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
@@ -590,6 +618,8 @@
sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
sActivityName = "MyTestActivity";
sPackageName = "test.package.name";
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+ /* value= */ 1);
when(mApplicationInfo.isSystemApp()).thenReturn(true);
// Managed profile exists.
List<UserInfo> profiles = new ArrayList<>();
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 61281ee..efe658a 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -33,6 +33,7 @@
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
<permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.MONITOR_INPUT"/>
<permission name="android.permission.PROVIDE_TRUST_AGENT"/>
<permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<permission name="android.permission.REAL_GET_TASKS"/>
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
index 337e153..17d614e 100644
--- a/data/etc/com.android.launcher3.xml
+++ b/data/etc/com.android.launcher3.xml
@@ -19,5 +19,6 @@
<permission name="android.permission.BIND_APPWIDGET"/>
<permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 49edcf7..5466ac8 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -62,6 +62,10 @@
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.launcher3">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.location.fused">
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
@@ -164,6 +168,7 @@
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.REGISTER_CALL_PROVIDER"/>
<permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
+ <permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
<permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
<permission name="android.permission.SET_TIME_ZONE"/>
<permission name="android.permission.SHUTDOWN"/>
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index d9d2eea..a7d0cb8 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -764,8 +764,9 @@
private @KeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
- private int mUserAuthenticationValidityDurationSeconds = -1;
- private @KeyProperties.AuthEnum int mUserAuthenticationType;
+ private int mUserAuthenticationValidityDurationSeconds = 0;
+ private @KeyProperties.AuthEnum int mUserAuthenticationType =
+ KeyProperties.AUTH_BIOMETRIC_STRONG;
private boolean mUserPresenceRequired = false;
private byte[] mAttestationChallenge = null;
private boolean mUniqueIdIncluded = false;
@@ -1240,7 +1241,8 @@
if (seconds == -1) {
return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
}
- return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+ return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_DEVICE_CREDENTIAL
+ | KeyProperties.AUTH_BIOMETRIC_STRONG);
}
/**
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 8120a93..2e793de 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -562,8 +562,9 @@
private @KeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
- private @KeyProperties.AuthEnum int mUserAuthenticationType;
- private int mUserAuthenticationValidityDurationSeconds = -1;
+ private int mUserAuthenticationValidityDurationSeconds = 0;
+ private @KeyProperties.AuthEnum int mUserAuthenticationType =
+ KeyProperties.AUTH_BIOMETRIC_STRONG;
private boolean mUserPresenceRequired = false;
private boolean mUserAuthenticationValidWhileOnBody;
private boolean mInvalidatedByBiometricEnrollment = true;
@@ -870,7 +871,8 @@
if (seconds == -1) {
return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
}
- return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+ return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_DEVICE_CREDENTIAL
+ | KeyProperties.AUTH_BIOMETRIC_STRONG);
}
/**
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 4ead253..670ef5e 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -82,6 +82,63 @@
}
}
+ private static void addSids(KeymasterArguments args, UserAuthArgs spec) {
+ // If both biometric and credential are accepted, then just use the root sid from gatekeeper
+ if (spec.getUserAuthenticationType() == (KeyProperties.AUTH_BIOMETRIC_STRONG
+ | KeyProperties.AUTH_DEVICE_CREDENTIAL)) {
+ if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+ KeymasterArguments.toUint64(spec.getBoundToSpecificSecureUserId()));
+ } else {
+ // The key is authorized for use for the specified amount of time after the user has
+ // authenticated. Whatever unlocks the secure lock screen should authorize this key.
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+ KeymasterArguments.toUint64(getRootSid()));
+ }
+ } else {
+ List<Long> sids = new ArrayList<>();
+ if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_BIOMETRIC_STRONG) != 0) {
+ final BiometricManager bm = KeyStore.getApplicationContext()
+ .getSystemService(BiometricManager.class);
+
+ // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer
+ // needed here.
+
+ final long[] biometricSids = bm.getAuthenticatorIds();
+
+ if (biometricSids.length == 0) {
+ throw new IllegalStateException(
+ "At least one biometric must be enrolled to create keys requiring user"
+ + " authentication for every use");
+ }
+
+ if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+ sids.add(spec.getBoundToSpecificSecureUserId());
+ } else if (spec.isInvalidatedByBiometricEnrollment()) {
+ // The biometric-only SIDs will change on biometric enrollment or removal of all
+ // enrolled templates, invalidating the key.
+ for (long sid : biometricSids) {
+ sids.add(sid);
+ }
+ } else {
+ // The root SID will *not* change on fingerprint enrollment, or removal of all
+ // enrolled fingerprints, allowing the key to remain valid.
+ sids.add(getRootSid());
+ }
+ } else if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_DEVICE_CREDENTIAL)
+ != 0) {
+ sids.add(getRootSid());
+ } else {
+ throw new IllegalStateException("Invalid or no authentication type specified.");
+ }
+
+ for (int i = 0; i < sids.size(); i++) {
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+ KeymasterArguments.toUint64(sids.get(i)));
+ }
+ }
+ }
+
/**
* Adds keymaster arguments to express the key's authorization policy supported by user
* authentication.
@@ -114,40 +171,7 @@
if (spec.getUserAuthenticationValidityDurationSeconds() == 0) {
// Every use of this key needs to be authorized by the user.
- final BiometricManager bm = KeyStore.getApplicationContext()
- .getSystemService(BiometricManager.class);
-
- // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer
- // needed here.
-
- final long[] biometricSids = bm.getAuthenticatorIds();
-
- if (biometricSids.length == 0) {
- throw new IllegalStateException(
- "At least one biometric must be enrolled to create keys requiring user"
- + " authentication for every use");
- }
-
- List<Long> sids = new ArrayList<>();
- if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
- sids.add(spec.getBoundToSpecificSecureUserId());
- } else if (spec.isInvalidatedByBiometricEnrollment()) {
- // The biometric-only SIDs will change on biometric enrollment or removal of all
- // enrolled templates, invalidating the key.
- for (long sid : biometricSids) {
- sids.add(sid);
- }
- } else {
- // The root SID will *not* change on fingerprint enrollment, or removal of all
- // enrolled fingerprints, allowing the key to remain valid.
- sids.add(getRootSid());
- }
-
- for (int i = 0; i < sids.size(); i++) {
- args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
- KeymasterArguments.toUint64(sids.get(i)));
- }
-
+ addSids(args, spec);
args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
if (spec.isUserAuthenticationValidWhileOnBody()) {
@@ -155,18 +179,8 @@
+ "supported for keys requiring fingerprint authentication");
}
} else {
- long sid;
- if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
- sid = spec.getBoundToSpecificSecureUserId();
- } else {
- // The key is authorized for use for the specified amount of time after the user has
- // authenticated. Whatever unlocks the secure lock screen should authorize this key.
- sid = getRootSid();
- }
- args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
- KeymasterArguments.toUint64(sid));
- args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
- KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_BIOMETRIC);
+ addSids(args, spec);
+ args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
spec.getUserAuthenticationValidityDurationSeconds());
if (spec.isUserAuthenticationValidWhileOnBody()) {
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index e748bd8..6c6c5c9 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -15,1474 +15,1478 @@
/* 11 */ {'C', 'a', 'r', 'i'},
/* 12 */ {'C', 'h', 'a', 'm'},
/* 13 */ {'C', 'h', 'e', 'r'},
- /* 14 */ {'C', 'o', 'p', 't'},
- /* 15 */ {'C', 'p', 'r', 't'},
- /* 16 */ {'C', 'y', 'r', 'l'},
- /* 17 */ {'D', 'e', 'v', 'a'},
- /* 18 */ {'E', 'g', 'y', 'p'},
- /* 19 */ {'E', 't', 'h', 'i'},
- /* 20 */ {'G', 'e', 'o', 'r'},
- /* 21 */ {'G', 'o', 'n', 'g'},
- /* 22 */ {'G', 'o', 'n', 'm'},
- /* 23 */ {'G', 'o', 't', 'h'},
- /* 24 */ {'G', 'r', 'e', 'k'},
- /* 25 */ {'G', 'u', 'j', 'r'},
- /* 26 */ {'G', 'u', 'r', 'u'},
- /* 27 */ {'H', 'a', 'n', 's'},
- /* 28 */ {'H', 'a', 'n', 't'},
- /* 29 */ {'H', 'a', 't', 'r'},
- /* 30 */ {'H', 'e', 'b', 'r'},
- /* 31 */ {'H', 'l', 'u', 'w'},
- /* 32 */ {'H', 'm', 'n', 'g'},
- /* 33 */ {'H', 'm', 'n', 'p'},
- /* 34 */ {'I', 't', 'a', 'l'},
- /* 35 */ {'J', 'p', 'a', 'n'},
- /* 36 */ {'K', 'a', 'l', 'i'},
- /* 37 */ {'K', 'a', 'n', 'a'},
- /* 38 */ {'K', 'h', 'a', 'r'},
- /* 39 */ {'K', 'h', 'm', 'r'},
- /* 40 */ {'K', 'n', 'd', 'a'},
- /* 41 */ {'K', 'o', 'r', 'e'},
- /* 42 */ {'L', 'a', 'n', 'a'},
- /* 43 */ {'L', 'a', 'o', 'o'},
- /* 44 */ {'L', 'a', 't', 'n'},
- /* 45 */ {'L', 'e', 'p', 'c'},
- /* 46 */ {'L', 'i', 'n', 'a'},
- /* 47 */ {'L', 'i', 's', 'u'},
- /* 48 */ {'L', 'y', 'c', 'i'},
- /* 49 */ {'L', 'y', 'd', 'i'},
- /* 50 */ {'M', 'a', 'n', 'd'},
- /* 51 */ {'M', 'a', 'n', 'i'},
- /* 52 */ {'M', 'e', 'r', 'c'},
- /* 53 */ {'M', 'l', 'y', 'm'},
- /* 54 */ {'M', 'o', 'n', 'g'},
- /* 55 */ {'M', 'r', 'o', 'o'},
- /* 56 */ {'M', 'y', 'm', 'r'},
- /* 57 */ {'N', 'a', 'r', 'b'},
- /* 58 */ {'N', 'k', 'o', 'o'},
- /* 59 */ {'N', 's', 'h', 'u'},
- /* 60 */ {'O', 'g', 'a', 'm'},
- /* 61 */ {'O', 'r', 'k', 'h'},
- /* 62 */ {'O', 'r', 'y', 'a'},
- /* 63 */ {'O', 's', 'g', 'e'},
- /* 64 */ {'P', 'a', 'u', 'c'},
- /* 65 */ {'P', 'h', 'l', 'i'},
- /* 66 */ {'P', 'h', 'n', 'x'},
- /* 67 */ {'P', 'l', 'r', 'd'},
- /* 68 */ {'P', 'r', 't', 'i'},
- /* 69 */ {'R', 'u', 'n', 'r'},
- /* 70 */ {'S', 'a', 'm', 'r'},
- /* 71 */ {'S', 'a', 'r', 'b'},
- /* 72 */ {'S', 'a', 'u', 'r'},
- /* 73 */ {'S', 'g', 'n', 'w'},
- /* 74 */ {'S', 'i', 'n', 'h'},
- /* 75 */ {'S', 'o', 'g', 'd'},
- /* 76 */ {'S', 'o', 'r', 'a'},
- /* 77 */ {'S', 'o', 'y', 'o'},
- /* 78 */ {'S', 'y', 'r', 'c'},
- /* 79 */ {'T', 'a', 'l', 'e'},
- /* 80 */ {'T', 'a', 'l', 'u'},
- /* 81 */ {'T', 'a', 'm', 'l'},
- /* 82 */ {'T', 'a', 'n', 'g'},
- /* 83 */ {'T', 'a', 'v', 't'},
- /* 84 */ {'T', 'e', 'l', 'u'},
- /* 85 */ {'T', 'f', 'n', 'g'},
- /* 86 */ {'T', 'h', 'a', 'a'},
- /* 87 */ {'T', 'h', 'a', 'i'},
- /* 88 */ {'T', 'i', 'b', 't'},
- /* 89 */ {'U', 'g', 'a', 'r'},
- /* 90 */ {'V', 'a', 'i', 'i'},
- /* 91 */ {'W', 'c', 'h', 'o'},
- /* 92 */ {'X', 'p', 'e', 'o'},
- /* 93 */ {'X', 's', 'u', 'x'},
- /* 94 */ {'Y', 'i', 'i', 'i'},
- /* 95 */ {'~', '~', '~', 'A'},
- /* 96 */ {'~', '~', '~', 'B'},
+ /* 14 */ {'C', 'h', 'r', 's'},
+ /* 15 */ {'C', 'o', 'p', 't'},
+ /* 16 */ {'C', 'p', 'r', 't'},
+ /* 17 */ {'C', 'y', 'r', 'l'},
+ /* 18 */ {'D', 'e', 'v', 'a'},
+ /* 19 */ {'E', 'g', 'y', 'p'},
+ /* 20 */ {'E', 't', 'h', 'i'},
+ /* 21 */ {'G', 'e', 'o', 'r'},
+ /* 22 */ {'G', 'o', 'n', 'g'},
+ /* 23 */ {'G', 'o', 'n', 'm'},
+ /* 24 */ {'G', 'o', 't', 'h'},
+ /* 25 */ {'G', 'r', 'e', 'k'},
+ /* 26 */ {'G', 'u', 'j', 'r'},
+ /* 27 */ {'G', 'u', 'r', 'u'},
+ /* 28 */ {'H', 'a', 'n', 's'},
+ /* 29 */ {'H', 'a', 'n', 't'},
+ /* 30 */ {'H', 'a', 't', 'r'},
+ /* 31 */ {'H', 'e', 'b', 'r'},
+ /* 32 */ {'H', 'l', 'u', 'w'},
+ /* 33 */ {'H', 'm', 'n', 'g'},
+ /* 34 */ {'H', 'm', 'n', 'p'},
+ /* 35 */ {'I', 't', 'a', 'l'},
+ /* 36 */ {'J', 'p', 'a', 'n'},
+ /* 37 */ {'K', 'a', 'l', 'i'},
+ /* 38 */ {'K', 'a', 'n', 'a'},
+ /* 39 */ {'K', 'h', 'a', 'r'},
+ /* 40 */ {'K', 'h', 'm', 'r'},
+ /* 41 */ {'K', 'i', 't', 's'},
+ /* 42 */ {'K', 'n', 'd', 'a'},
+ /* 43 */ {'K', 'o', 'r', 'e'},
+ /* 44 */ {'L', 'a', 'n', 'a'},
+ /* 45 */ {'L', 'a', 'o', 'o'},
+ /* 46 */ {'L', 'a', 't', 'n'},
+ /* 47 */ {'L', 'e', 'p', 'c'},
+ /* 48 */ {'L', 'i', 'n', 'a'},
+ /* 49 */ {'L', 'i', 's', 'u'},
+ /* 50 */ {'L', 'y', 'c', 'i'},
+ /* 51 */ {'L', 'y', 'd', 'i'},
+ /* 52 */ {'M', 'a', 'n', 'd'},
+ /* 53 */ {'M', 'a', 'n', 'i'},
+ /* 54 */ {'M', 'e', 'r', 'c'},
+ /* 55 */ {'M', 'l', 'y', 'm'},
+ /* 56 */ {'M', 'o', 'n', 'g'},
+ /* 57 */ {'M', 'r', 'o', 'o'},
+ /* 58 */ {'M', 'y', 'm', 'r'},
+ /* 59 */ {'N', 'a', 'r', 'b'},
+ /* 60 */ {'N', 'k', 'o', 'o'},
+ /* 61 */ {'N', 's', 'h', 'u'},
+ /* 62 */ {'O', 'g', 'a', 'm'},
+ /* 63 */ {'O', 'r', 'k', 'h'},
+ /* 64 */ {'O', 'r', 'y', 'a'},
+ /* 65 */ {'O', 's', 'g', 'e'},
+ /* 66 */ {'P', 'a', 'u', 'c'},
+ /* 67 */ {'P', 'h', 'l', 'i'},
+ /* 68 */ {'P', 'h', 'n', 'x'},
+ /* 69 */ {'P', 'l', 'r', 'd'},
+ /* 70 */ {'P', 'r', 't', 'i'},
+ /* 71 */ {'R', 'u', 'n', 'r'},
+ /* 72 */ {'S', 'a', 'm', 'r'},
+ /* 73 */ {'S', 'a', 'r', 'b'},
+ /* 74 */ {'S', 'a', 'u', 'r'},
+ /* 75 */ {'S', 'g', 'n', 'w'},
+ /* 76 */ {'S', 'i', 'n', 'h'},
+ /* 77 */ {'S', 'o', 'g', 'd'},
+ /* 78 */ {'S', 'o', 'r', 'a'},
+ /* 79 */ {'S', 'o', 'y', 'o'},
+ /* 80 */ {'S', 'y', 'r', 'c'},
+ /* 81 */ {'T', 'a', 'l', 'e'},
+ /* 82 */ {'T', 'a', 'l', 'u'},
+ /* 83 */ {'T', 'a', 'm', 'l'},
+ /* 84 */ {'T', 'a', 'n', 'g'},
+ /* 85 */ {'T', 'a', 'v', 't'},
+ /* 86 */ {'T', 'e', 'l', 'u'},
+ /* 87 */ {'T', 'f', 'n', 'g'},
+ /* 88 */ {'T', 'h', 'a', 'a'},
+ /* 89 */ {'T', 'h', 'a', 'i'},
+ /* 90 */ {'T', 'i', 'b', 't'},
+ /* 91 */ {'U', 'g', 'a', 'r'},
+ /* 92 */ {'V', 'a', 'i', 'i'},
+ /* 93 */ {'W', 'c', 'h', 'o'},
+ /* 94 */ {'X', 'p', 'e', 'o'},
+ /* 95 */ {'X', 's', 'u', 'x'},
+ /* 96 */ {'Y', 'i', 'i', 'i'},
+ /* 97 */ {'~', '~', '~', 'A'},
+ /* 98 */ {'~', '~', '~', 'B'},
};
const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
- {0x61610000u, 44u}, // aa -> Latn
- {0xA0000000u, 44u}, // aai -> Latn
- {0xA8000000u, 44u}, // aak -> Latn
- {0xD0000000u, 44u}, // aau -> Latn
- {0x61620000u, 16u}, // ab -> Cyrl
- {0xA0200000u, 44u}, // abi -> Latn
- {0xC0200000u, 16u}, // abq -> Cyrl
- {0xC4200000u, 44u}, // abr -> Latn
- {0xCC200000u, 44u}, // abt -> Latn
- {0xE0200000u, 44u}, // aby -> Latn
- {0x8C400000u, 44u}, // acd -> Latn
- {0x90400000u, 44u}, // ace -> Latn
- {0x9C400000u, 44u}, // ach -> Latn
- {0x80600000u, 44u}, // ada -> Latn
- {0x90600000u, 44u}, // ade -> Latn
- {0xA4600000u, 44u}, // adj -> Latn
- {0xBC600000u, 88u}, // adp -> Tibt
- {0xE0600000u, 16u}, // ady -> Cyrl
- {0xE4600000u, 44u}, // adz -> Latn
+ {0x61610000u, 46u}, // aa -> Latn
+ {0xA0000000u, 46u}, // aai -> Latn
+ {0xA8000000u, 46u}, // aak -> Latn
+ {0xD0000000u, 46u}, // aau -> Latn
+ {0x61620000u, 17u}, // ab -> Cyrl
+ {0xA0200000u, 46u}, // abi -> Latn
+ {0xC0200000u, 17u}, // abq -> Cyrl
+ {0xC4200000u, 46u}, // abr -> Latn
+ {0xCC200000u, 46u}, // abt -> Latn
+ {0xE0200000u, 46u}, // aby -> Latn
+ {0x8C400000u, 46u}, // acd -> Latn
+ {0x90400000u, 46u}, // ace -> Latn
+ {0x9C400000u, 46u}, // ach -> Latn
+ {0x80600000u, 46u}, // ada -> Latn
+ {0x90600000u, 46u}, // ade -> Latn
+ {0xA4600000u, 46u}, // adj -> Latn
+ {0xBC600000u, 90u}, // adp -> Tibt
+ {0xE0600000u, 17u}, // ady -> Cyrl
+ {0xE4600000u, 46u}, // adz -> Latn
{0x61650000u, 4u}, // ae -> Avst
{0x84800000u, 1u}, // aeb -> Arab
- {0xE0800000u, 44u}, // aey -> Latn
- {0x61660000u, 44u}, // af -> Latn
- {0x88C00000u, 44u}, // agc -> Latn
- {0x8CC00000u, 44u}, // agd -> Latn
- {0x98C00000u, 44u}, // agg -> Latn
- {0xB0C00000u, 44u}, // agm -> Latn
- {0xB8C00000u, 44u}, // ago -> Latn
- {0xC0C00000u, 44u}, // agq -> Latn
- {0x80E00000u, 44u}, // aha -> Latn
- {0xACE00000u, 44u}, // ahl -> Latn
+ {0xE0800000u, 46u}, // aey -> Latn
+ {0x61660000u, 46u}, // af -> Latn
+ {0x88C00000u, 46u}, // agc -> Latn
+ {0x8CC00000u, 46u}, // agd -> Latn
+ {0x98C00000u, 46u}, // agg -> Latn
+ {0xB0C00000u, 46u}, // agm -> Latn
+ {0xB8C00000u, 46u}, // ago -> Latn
+ {0xC0C00000u, 46u}, // agq -> Latn
+ {0x80E00000u, 46u}, // aha -> Latn
+ {0xACE00000u, 46u}, // ahl -> Latn
{0xB8E00000u, 0u}, // aho -> Ahom
- {0x99200000u, 44u}, // ajg -> Latn
- {0x616B0000u, 44u}, // ak -> Latn
- {0xA9400000u, 93u}, // akk -> Xsux
- {0x81600000u, 44u}, // ala -> Latn
- {0xA1600000u, 44u}, // ali -> Latn
- {0xB5600000u, 44u}, // aln -> Latn
- {0xCD600000u, 16u}, // alt -> Cyrl
- {0x616D0000u, 19u}, // am -> Ethi
- {0xB1800000u, 44u}, // amm -> Latn
- {0xB5800000u, 44u}, // amn -> Latn
- {0xB9800000u, 44u}, // amo -> Latn
- {0xBD800000u, 44u}, // amp -> Latn
- {0x616E0000u, 44u}, // an -> Latn
- {0x89A00000u, 44u}, // anc -> Latn
- {0xA9A00000u, 44u}, // ank -> Latn
- {0xB5A00000u, 44u}, // ann -> Latn
- {0xE1A00000u, 44u}, // any -> Latn
- {0xA5C00000u, 44u}, // aoj -> Latn
- {0xB1C00000u, 44u}, // aom -> Latn
- {0xE5C00000u, 44u}, // aoz -> Latn
+ {0x99200000u, 46u}, // ajg -> Latn
+ {0x616B0000u, 46u}, // ak -> Latn
+ {0xA9400000u, 95u}, // akk -> Xsux
+ {0x81600000u, 46u}, // ala -> Latn
+ {0xA1600000u, 46u}, // ali -> Latn
+ {0xB5600000u, 46u}, // aln -> Latn
+ {0xCD600000u, 17u}, // alt -> Cyrl
+ {0x616D0000u, 20u}, // am -> Ethi
+ {0xB1800000u, 46u}, // amm -> Latn
+ {0xB5800000u, 46u}, // amn -> Latn
+ {0xB9800000u, 46u}, // amo -> Latn
+ {0xBD800000u, 46u}, // amp -> Latn
+ {0x616E0000u, 46u}, // an -> Latn
+ {0x89A00000u, 46u}, // anc -> Latn
+ {0xA9A00000u, 46u}, // ank -> Latn
+ {0xB5A00000u, 46u}, // ann -> Latn
+ {0xE1A00000u, 46u}, // any -> Latn
+ {0xA5C00000u, 46u}, // aoj -> Latn
+ {0xB1C00000u, 46u}, // aom -> Latn
+ {0xE5C00000u, 46u}, // aoz -> Latn
{0x89E00000u, 1u}, // apc -> Arab
{0x8DE00000u, 1u}, // apd -> Arab
- {0x91E00000u, 44u}, // ape -> Latn
- {0xC5E00000u, 44u}, // apr -> Latn
- {0xC9E00000u, 44u}, // aps -> Latn
- {0xE5E00000u, 44u}, // apz -> Latn
+ {0x91E00000u, 46u}, // ape -> Latn
+ {0xC5E00000u, 46u}, // apr -> Latn
+ {0xC9E00000u, 46u}, // aps -> Latn
+ {0xE5E00000u, 46u}, // apz -> Latn
{0x61720000u, 1u}, // ar -> Arab
- {0x61725842u, 96u}, // ar-XB -> ~~~B
+ {0x61725842u, 98u}, // ar-XB -> ~~~B
{0x8A200000u, 2u}, // arc -> Armi
- {0x9E200000u, 44u}, // arh -> Latn
- {0xB6200000u, 44u}, // arn -> Latn
- {0xBA200000u, 44u}, // aro -> Latn
+ {0x9E200000u, 46u}, // arh -> Latn
+ {0xB6200000u, 46u}, // arn -> Latn
+ {0xBA200000u, 46u}, // aro -> Latn
{0xC2200000u, 1u}, // arq -> Arab
{0xCA200000u, 1u}, // ars -> Arab
{0xE2200000u, 1u}, // ary -> Arab
{0xE6200000u, 1u}, // arz -> Arab
{0x61730000u, 7u}, // as -> Beng
- {0x82400000u, 44u}, // asa -> Latn
- {0x92400000u, 73u}, // ase -> Sgnw
- {0x9A400000u, 44u}, // asg -> Latn
- {0xBA400000u, 44u}, // aso -> Latn
- {0xCE400000u, 44u}, // ast -> Latn
- {0x82600000u, 44u}, // ata -> Latn
- {0x9A600000u, 44u}, // atg -> Latn
- {0xA6600000u, 44u}, // atj -> Latn
- {0xE2800000u, 44u}, // auy -> Latn
- {0x61760000u, 16u}, // av -> Cyrl
+ {0x82400000u, 46u}, // asa -> Latn
+ {0x92400000u, 75u}, // ase -> Sgnw
+ {0x9A400000u, 46u}, // asg -> Latn
+ {0xBA400000u, 46u}, // aso -> Latn
+ {0xCE400000u, 46u}, // ast -> Latn
+ {0x82600000u, 46u}, // ata -> Latn
+ {0x9A600000u, 46u}, // atg -> Latn
+ {0xA6600000u, 46u}, // atj -> Latn
+ {0xE2800000u, 46u}, // auy -> Latn
+ {0x61760000u, 17u}, // av -> Cyrl
{0xAEA00000u, 1u}, // avl -> Arab
- {0xB6A00000u, 44u}, // avn -> Latn
- {0xCEA00000u, 44u}, // avt -> Latn
- {0xD2A00000u, 44u}, // avu -> Latn
- {0x82C00000u, 17u}, // awa -> Deva
- {0x86C00000u, 44u}, // awb -> Latn
- {0xBAC00000u, 44u}, // awo -> Latn
- {0xDEC00000u, 44u}, // awx -> Latn
- {0x61790000u, 44u}, // ay -> Latn
- {0x87000000u, 44u}, // ayb -> Latn
- {0x617A0000u, 44u}, // az -> Latn
+ {0xB6A00000u, 46u}, // avn -> Latn
+ {0xCEA00000u, 46u}, // avt -> Latn
+ {0xD2A00000u, 46u}, // avu -> Latn
+ {0x82C00000u, 18u}, // awa -> Deva
+ {0x86C00000u, 46u}, // awb -> Latn
+ {0xBAC00000u, 46u}, // awo -> Latn
+ {0xDEC00000u, 46u}, // awx -> Latn
+ {0x61790000u, 46u}, // ay -> Latn
+ {0x87000000u, 46u}, // ayb -> Latn
+ {0x617A0000u, 46u}, // az -> Latn
{0x617A4951u, 1u}, // az-IQ -> Arab
{0x617A4952u, 1u}, // az-IR -> Arab
- {0x617A5255u, 16u}, // az-RU -> Cyrl
- {0x62610000u, 16u}, // ba -> Cyrl
+ {0x617A5255u, 17u}, // az-RU -> Cyrl
+ {0x62610000u, 17u}, // ba -> Cyrl
{0xAC010000u, 1u}, // bal -> Arab
- {0xB4010000u, 44u}, // ban -> Latn
- {0xBC010000u, 17u}, // bap -> Deva
- {0xC4010000u, 44u}, // bar -> Latn
- {0xC8010000u, 44u}, // bas -> Latn
- {0xD4010000u, 44u}, // bav -> Latn
+ {0xB4010000u, 46u}, // ban -> Latn
+ {0xBC010000u, 18u}, // bap -> Deva
+ {0xC4010000u, 46u}, // bar -> Latn
+ {0xC8010000u, 46u}, // bas -> Latn
+ {0xD4010000u, 46u}, // bav -> Latn
{0xDC010000u, 5u}, // bax -> Bamu
- {0x80210000u, 44u}, // bba -> Latn
- {0x84210000u, 44u}, // bbb -> Latn
- {0x88210000u, 44u}, // bbc -> Latn
- {0x8C210000u, 44u}, // bbd -> Latn
- {0xA4210000u, 44u}, // bbj -> Latn
- {0xBC210000u, 44u}, // bbp -> Latn
- {0xC4210000u, 44u}, // bbr -> Latn
- {0x94410000u, 44u}, // bcf -> Latn
- {0x9C410000u, 44u}, // bch -> Latn
- {0xA0410000u, 44u}, // bci -> Latn
- {0xB0410000u, 44u}, // bcm -> Latn
- {0xB4410000u, 44u}, // bcn -> Latn
- {0xB8410000u, 44u}, // bco -> Latn
- {0xC0410000u, 19u}, // bcq -> Ethi
- {0xD0410000u, 44u}, // bcu -> Latn
- {0x8C610000u, 44u}, // bdd -> Latn
- {0x62650000u, 16u}, // be -> Cyrl
- {0x94810000u, 44u}, // bef -> Latn
- {0x9C810000u, 44u}, // beh -> Latn
+ {0x80210000u, 46u}, // bba -> Latn
+ {0x84210000u, 46u}, // bbb -> Latn
+ {0x88210000u, 46u}, // bbc -> Latn
+ {0x8C210000u, 46u}, // bbd -> Latn
+ {0xA4210000u, 46u}, // bbj -> Latn
+ {0xBC210000u, 46u}, // bbp -> Latn
+ {0xC4210000u, 46u}, // bbr -> Latn
+ {0x94410000u, 46u}, // bcf -> Latn
+ {0x9C410000u, 46u}, // bch -> Latn
+ {0xA0410000u, 46u}, // bci -> Latn
+ {0xB0410000u, 46u}, // bcm -> Latn
+ {0xB4410000u, 46u}, // bcn -> Latn
+ {0xB8410000u, 46u}, // bco -> Latn
+ {0xC0410000u, 20u}, // bcq -> Ethi
+ {0xD0410000u, 46u}, // bcu -> Latn
+ {0x8C610000u, 46u}, // bdd -> Latn
+ {0x62650000u, 17u}, // be -> Cyrl
+ {0x94810000u, 46u}, // bef -> Latn
+ {0x9C810000u, 46u}, // beh -> Latn
{0xA4810000u, 1u}, // bej -> Arab
- {0xB0810000u, 44u}, // bem -> Latn
- {0xCC810000u, 44u}, // bet -> Latn
- {0xD8810000u, 44u}, // bew -> Latn
- {0xDC810000u, 44u}, // bex -> Latn
- {0xE4810000u, 44u}, // bez -> Latn
- {0x8CA10000u, 44u}, // bfd -> Latn
- {0xC0A10000u, 81u}, // bfq -> Taml
+ {0xB0810000u, 46u}, // bem -> Latn
+ {0xCC810000u, 46u}, // bet -> Latn
+ {0xD8810000u, 46u}, // bew -> Latn
+ {0xDC810000u, 46u}, // bex -> Latn
+ {0xE4810000u, 46u}, // bez -> Latn
+ {0x8CA10000u, 46u}, // bfd -> Latn
+ {0xC0A10000u, 83u}, // bfq -> Taml
{0xCCA10000u, 1u}, // bft -> Arab
- {0xE0A10000u, 17u}, // bfy -> Deva
- {0x62670000u, 16u}, // bg -> Cyrl
- {0x88C10000u, 17u}, // bgc -> Deva
+ {0xE0A10000u, 18u}, // bfy -> Deva
+ {0x62670000u, 17u}, // bg -> Cyrl
+ {0x88C10000u, 18u}, // bgc -> Deva
{0xB4C10000u, 1u}, // bgn -> Arab
- {0xDCC10000u, 24u}, // bgx -> Grek
- {0x84E10000u, 17u}, // bhb -> Deva
- {0x98E10000u, 44u}, // bhg -> Latn
- {0xA0E10000u, 17u}, // bhi -> Deva
- {0xACE10000u, 44u}, // bhl -> Latn
- {0xB8E10000u, 17u}, // bho -> Deva
- {0xE0E10000u, 44u}, // bhy -> Latn
- {0x62690000u, 44u}, // bi -> Latn
- {0x85010000u, 44u}, // bib -> Latn
- {0x99010000u, 44u}, // big -> Latn
- {0xA9010000u, 44u}, // bik -> Latn
- {0xB1010000u, 44u}, // bim -> Latn
- {0xB5010000u, 44u}, // bin -> Latn
- {0xB9010000u, 44u}, // bio -> Latn
- {0xC1010000u, 44u}, // biq -> Latn
- {0x9D210000u, 44u}, // bjh -> Latn
- {0xA1210000u, 19u}, // bji -> Ethi
- {0xA5210000u, 17u}, // bjj -> Deva
- {0xB5210000u, 44u}, // bjn -> Latn
- {0xB9210000u, 44u}, // bjo -> Latn
- {0xC5210000u, 44u}, // bjr -> Latn
- {0xCD210000u, 44u}, // bjt -> Latn
- {0xE5210000u, 44u}, // bjz -> Latn
- {0x89410000u, 44u}, // bkc -> Latn
- {0xB1410000u, 44u}, // bkm -> Latn
- {0xC1410000u, 44u}, // bkq -> Latn
- {0xD1410000u, 44u}, // bku -> Latn
- {0xD5410000u, 44u}, // bkv -> Latn
- {0xCD610000u, 83u}, // blt -> Tavt
- {0x626D0000u, 44u}, // bm -> Latn
- {0x9D810000u, 44u}, // bmh -> Latn
- {0xA9810000u, 44u}, // bmk -> Latn
- {0xC1810000u, 44u}, // bmq -> Latn
- {0xD1810000u, 44u}, // bmu -> Latn
+ {0xDCC10000u, 25u}, // bgx -> Grek
+ {0x84E10000u, 18u}, // bhb -> Deva
+ {0x98E10000u, 46u}, // bhg -> Latn
+ {0xA0E10000u, 18u}, // bhi -> Deva
+ {0xACE10000u, 46u}, // bhl -> Latn
+ {0xB8E10000u, 18u}, // bho -> Deva
+ {0xE0E10000u, 46u}, // bhy -> Latn
+ {0x62690000u, 46u}, // bi -> Latn
+ {0x85010000u, 46u}, // bib -> Latn
+ {0x99010000u, 46u}, // big -> Latn
+ {0xA9010000u, 46u}, // bik -> Latn
+ {0xB1010000u, 46u}, // bim -> Latn
+ {0xB5010000u, 46u}, // bin -> Latn
+ {0xB9010000u, 46u}, // bio -> Latn
+ {0xC1010000u, 46u}, // biq -> Latn
+ {0x9D210000u, 46u}, // bjh -> Latn
+ {0xA1210000u, 20u}, // bji -> Ethi
+ {0xA5210000u, 18u}, // bjj -> Deva
+ {0xB5210000u, 46u}, // bjn -> Latn
+ {0xB9210000u, 46u}, // bjo -> Latn
+ {0xC5210000u, 46u}, // bjr -> Latn
+ {0xCD210000u, 46u}, // bjt -> Latn
+ {0xE5210000u, 46u}, // bjz -> Latn
+ {0x89410000u, 46u}, // bkc -> Latn
+ {0xB1410000u, 46u}, // bkm -> Latn
+ {0xC1410000u, 46u}, // bkq -> Latn
+ {0xD1410000u, 46u}, // bku -> Latn
+ {0xD5410000u, 46u}, // bkv -> Latn
+ {0xCD610000u, 85u}, // blt -> Tavt
+ {0x626D0000u, 46u}, // bm -> Latn
+ {0x9D810000u, 46u}, // bmh -> Latn
+ {0xA9810000u, 46u}, // bmk -> Latn
+ {0xC1810000u, 46u}, // bmq -> Latn
+ {0xD1810000u, 46u}, // bmu -> Latn
{0x626E0000u, 7u}, // bn -> Beng
- {0x99A10000u, 44u}, // bng -> Latn
- {0xB1A10000u, 44u}, // bnm -> Latn
- {0xBDA10000u, 44u}, // bnp -> Latn
- {0x626F0000u, 88u}, // bo -> Tibt
- {0xA5C10000u, 44u}, // boj -> Latn
- {0xB1C10000u, 44u}, // bom -> Latn
- {0xB5C10000u, 44u}, // bon -> Latn
+ {0x99A10000u, 46u}, // bng -> Latn
+ {0xB1A10000u, 46u}, // bnm -> Latn
+ {0xBDA10000u, 46u}, // bnp -> Latn
+ {0x626F0000u, 90u}, // bo -> Tibt
+ {0xA5C10000u, 46u}, // boj -> Latn
+ {0xB1C10000u, 46u}, // bom -> Latn
+ {0xB5C10000u, 46u}, // bon -> Latn
{0xE1E10000u, 7u}, // bpy -> Beng
- {0x8A010000u, 44u}, // bqc -> Latn
+ {0x8A010000u, 46u}, // bqc -> Latn
{0xA2010000u, 1u}, // bqi -> Arab
- {0xBE010000u, 44u}, // bqp -> Latn
- {0xD6010000u, 44u}, // bqv -> Latn
- {0x62720000u, 44u}, // br -> Latn
- {0x82210000u, 17u}, // bra -> Deva
+ {0xBE010000u, 46u}, // bqp -> Latn
+ {0xD6010000u, 46u}, // bqv -> Latn
+ {0x62720000u, 46u}, // br -> Latn
+ {0x82210000u, 18u}, // bra -> Deva
{0x9E210000u, 1u}, // brh -> Arab
- {0xDE210000u, 17u}, // brx -> Deva
- {0xE6210000u, 44u}, // brz -> Latn
- {0x62730000u, 44u}, // bs -> Latn
- {0xA6410000u, 44u}, // bsj -> Latn
+ {0xDE210000u, 18u}, // brx -> Deva
+ {0xE6210000u, 46u}, // brz -> Latn
+ {0x62730000u, 46u}, // bs -> Latn
+ {0xA6410000u, 46u}, // bsj -> Latn
{0xC2410000u, 6u}, // bsq -> Bass
- {0xCA410000u, 44u}, // bss -> Latn
- {0xCE410000u, 19u}, // bst -> Ethi
- {0xBA610000u, 44u}, // bto -> Latn
- {0xCE610000u, 44u}, // btt -> Latn
- {0xD6610000u, 17u}, // btv -> Deva
- {0x82810000u, 16u}, // bua -> Cyrl
- {0x8A810000u, 44u}, // buc -> Latn
- {0x8E810000u, 44u}, // bud -> Latn
- {0x9A810000u, 44u}, // bug -> Latn
- {0xAA810000u, 44u}, // buk -> Latn
- {0xB2810000u, 44u}, // bum -> Latn
- {0xBA810000u, 44u}, // buo -> Latn
- {0xCA810000u, 44u}, // bus -> Latn
- {0xD2810000u, 44u}, // buu -> Latn
- {0x86A10000u, 44u}, // bvb -> Latn
- {0x8EC10000u, 44u}, // bwd -> Latn
- {0xC6C10000u, 44u}, // bwr -> Latn
- {0x9EE10000u, 44u}, // bxh -> Latn
- {0x93010000u, 44u}, // bye -> Latn
- {0xB7010000u, 19u}, // byn -> Ethi
- {0xC7010000u, 44u}, // byr -> Latn
- {0xCB010000u, 44u}, // bys -> Latn
- {0xD7010000u, 44u}, // byv -> Latn
- {0xDF010000u, 44u}, // byx -> Latn
- {0x83210000u, 44u}, // bza -> Latn
- {0x93210000u, 44u}, // bze -> Latn
- {0x97210000u, 44u}, // bzf -> Latn
- {0x9F210000u, 44u}, // bzh -> Latn
- {0xDB210000u, 44u}, // bzw -> Latn
- {0x63610000u, 44u}, // ca -> Latn
- {0xB4020000u, 44u}, // can -> Latn
- {0xA4220000u, 44u}, // cbj -> Latn
- {0x9C420000u, 44u}, // cch -> Latn
+ {0xCA410000u, 46u}, // bss -> Latn
+ {0xCE410000u, 20u}, // bst -> Ethi
+ {0xBA610000u, 46u}, // bto -> Latn
+ {0xCE610000u, 46u}, // btt -> Latn
+ {0xD6610000u, 18u}, // btv -> Deva
+ {0x82810000u, 17u}, // bua -> Cyrl
+ {0x8A810000u, 46u}, // buc -> Latn
+ {0x8E810000u, 46u}, // bud -> Latn
+ {0x9A810000u, 46u}, // bug -> Latn
+ {0xAA810000u, 46u}, // buk -> Latn
+ {0xB2810000u, 46u}, // bum -> Latn
+ {0xBA810000u, 46u}, // buo -> Latn
+ {0xCA810000u, 46u}, // bus -> Latn
+ {0xD2810000u, 46u}, // buu -> Latn
+ {0x86A10000u, 46u}, // bvb -> Latn
+ {0x8EC10000u, 46u}, // bwd -> Latn
+ {0xC6C10000u, 46u}, // bwr -> Latn
+ {0x9EE10000u, 46u}, // bxh -> Latn
+ {0x93010000u, 46u}, // bye -> Latn
+ {0xB7010000u, 20u}, // byn -> Ethi
+ {0xC7010000u, 46u}, // byr -> Latn
+ {0xCB010000u, 46u}, // bys -> Latn
+ {0xD7010000u, 46u}, // byv -> Latn
+ {0xDF010000u, 46u}, // byx -> Latn
+ {0x83210000u, 46u}, // bza -> Latn
+ {0x93210000u, 46u}, // bze -> Latn
+ {0x97210000u, 46u}, // bzf -> Latn
+ {0x9F210000u, 46u}, // bzh -> Latn
+ {0xDB210000u, 46u}, // bzw -> Latn
+ {0x63610000u, 46u}, // ca -> Latn
+ {0xB4020000u, 46u}, // can -> Latn
+ {0xA4220000u, 46u}, // cbj -> Latn
+ {0x9C420000u, 46u}, // cch -> Latn
{0xBC420000u, 9u}, // ccp -> Cakm
- {0x63650000u, 16u}, // ce -> Cyrl
- {0x84820000u, 44u}, // ceb -> Latn
- {0x80A20000u, 44u}, // cfa -> Latn
- {0x98C20000u, 44u}, // cgg -> Latn
- {0x63680000u, 44u}, // ch -> Latn
- {0xA8E20000u, 44u}, // chk -> Latn
- {0xB0E20000u, 16u}, // chm -> Cyrl
- {0xB8E20000u, 44u}, // cho -> Latn
- {0xBCE20000u, 44u}, // chp -> Latn
+ {0x63650000u, 17u}, // ce -> Cyrl
+ {0x84820000u, 46u}, // ceb -> Latn
+ {0x80A20000u, 46u}, // cfa -> Latn
+ {0x98C20000u, 46u}, // cgg -> Latn
+ {0x63680000u, 46u}, // ch -> Latn
+ {0xA8E20000u, 46u}, // chk -> Latn
+ {0xB0E20000u, 17u}, // chm -> Cyrl
+ {0xB8E20000u, 46u}, // cho -> Latn
+ {0xBCE20000u, 46u}, // chp -> Latn
{0xC4E20000u, 13u}, // chr -> Cher
- {0x89020000u, 44u}, // cic -> Latn
+ {0x89020000u, 46u}, // cic -> Latn
{0x81220000u, 1u}, // cja -> Arab
{0xB1220000u, 12u}, // cjm -> Cham
- {0xD5220000u, 44u}, // cjv -> Latn
+ {0xD5220000u, 46u}, // cjv -> Latn
{0x85420000u, 1u}, // ckb -> Arab
- {0xAD420000u, 44u}, // ckl -> Latn
- {0xB9420000u, 44u}, // cko -> Latn
- {0xE1420000u, 44u}, // cky -> Latn
- {0x81620000u, 44u}, // cla -> Latn
- {0x91820000u, 44u}, // cme -> Latn
- {0x99820000u, 77u}, // cmg -> Soyo
- {0x636F0000u, 44u}, // co -> Latn
- {0xBDC20000u, 14u}, // cop -> Copt
- {0xC9E20000u, 44u}, // cps -> Latn
+ {0xAD420000u, 46u}, // ckl -> Latn
+ {0xB9420000u, 46u}, // cko -> Latn
+ {0xE1420000u, 46u}, // cky -> Latn
+ {0x81620000u, 46u}, // cla -> Latn
+ {0x91820000u, 46u}, // cme -> Latn
+ {0x99820000u, 79u}, // cmg -> Soyo
+ {0x636F0000u, 46u}, // co -> Latn
+ {0xBDC20000u, 15u}, // cop -> Copt
+ {0xC9E20000u, 46u}, // cps -> Latn
{0x63720000u, 10u}, // cr -> Cans
- {0x9E220000u, 16u}, // crh -> Cyrl
+ {0x9E220000u, 17u}, // crh -> Cyrl
{0xA6220000u, 10u}, // crj -> Cans
{0xAA220000u, 10u}, // crk -> Cans
{0xAE220000u, 10u}, // crl -> Cans
{0xB2220000u, 10u}, // crm -> Cans
- {0xCA220000u, 44u}, // crs -> Latn
- {0x63730000u, 44u}, // cs -> Latn
- {0x86420000u, 44u}, // csb -> Latn
+ {0xCA220000u, 46u}, // crs -> Latn
+ {0x63730000u, 46u}, // cs -> Latn
+ {0x86420000u, 46u}, // csb -> Latn
{0xDA420000u, 10u}, // csw -> Cans
- {0x8E620000u, 64u}, // ctd -> Pauc
- {0x63750000u, 16u}, // cu -> Cyrl
- {0x63760000u, 16u}, // cv -> Cyrl
- {0x63790000u, 44u}, // cy -> Latn
- {0x64610000u, 44u}, // da -> Latn
- {0x8C030000u, 44u}, // dad -> Latn
- {0x94030000u, 44u}, // daf -> Latn
- {0x98030000u, 44u}, // dag -> Latn
- {0x9C030000u, 44u}, // dah -> Latn
- {0xA8030000u, 44u}, // dak -> Latn
- {0xC4030000u, 16u}, // dar -> Cyrl
- {0xD4030000u, 44u}, // dav -> Latn
- {0x8C230000u, 44u}, // dbd -> Latn
- {0xC0230000u, 44u}, // dbq -> Latn
+ {0x8E620000u, 66u}, // ctd -> Pauc
+ {0x63750000u, 17u}, // cu -> Cyrl
+ {0x63760000u, 17u}, // cv -> Cyrl
+ {0x63790000u, 46u}, // cy -> Latn
+ {0x64610000u, 46u}, // da -> Latn
+ {0x8C030000u, 46u}, // dad -> Latn
+ {0x94030000u, 46u}, // daf -> Latn
+ {0x98030000u, 46u}, // dag -> Latn
+ {0x9C030000u, 46u}, // dah -> Latn
+ {0xA8030000u, 46u}, // dak -> Latn
+ {0xC4030000u, 17u}, // dar -> Cyrl
+ {0xD4030000u, 46u}, // dav -> Latn
+ {0x8C230000u, 46u}, // dbd -> Latn
+ {0xC0230000u, 46u}, // dbq -> Latn
{0x88430000u, 1u}, // dcc -> Arab
- {0xB4630000u, 44u}, // ddn -> Latn
- {0x64650000u, 44u}, // de -> Latn
- {0x8C830000u, 44u}, // ded -> Latn
- {0xB4830000u, 44u}, // den -> Latn
- {0x80C30000u, 44u}, // dga -> Latn
- {0x9CC30000u, 44u}, // dgh -> Latn
- {0xA0C30000u, 44u}, // dgi -> Latn
+ {0xB4630000u, 46u}, // ddn -> Latn
+ {0x64650000u, 46u}, // de -> Latn
+ {0x8C830000u, 46u}, // ded -> Latn
+ {0xB4830000u, 46u}, // den -> Latn
+ {0x80C30000u, 46u}, // dga -> Latn
+ {0x9CC30000u, 46u}, // dgh -> Latn
+ {0xA0C30000u, 46u}, // dgi -> Latn
{0xACC30000u, 1u}, // dgl -> Arab
- {0xC4C30000u, 44u}, // dgr -> Latn
- {0xE4C30000u, 44u}, // dgz -> Latn
- {0x81030000u, 44u}, // dia -> Latn
- {0x91230000u, 44u}, // dje -> Latn
- {0xA5A30000u, 44u}, // dnj -> Latn
- {0x85C30000u, 44u}, // dob -> Latn
+ {0xC4C30000u, 46u}, // dgr -> Latn
+ {0xE4C30000u, 46u}, // dgz -> Latn
+ {0x81030000u, 46u}, // dia -> Latn
+ {0x91230000u, 46u}, // dje -> Latn
+ {0xA5A30000u, 46u}, // dnj -> Latn
+ {0x85C30000u, 46u}, // dob -> Latn
{0xA1C30000u, 1u}, // doi -> Arab
- {0xBDC30000u, 44u}, // dop -> Latn
- {0xD9C30000u, 44u}, // dow -> Latn
- {0x9E230000u, 54u}, // drh -> Mong
- {0xA2230000u, 44u}, // dri -> Latn
- {0xCA230000u, 19u}, // drs -> Ethi
- {0x86430000u, 44u}, // dsb -> Latn
- {0xB2630000u, 44u}, // dtm -> Latn
- {0xBE630000u, 44u}, // dtp -> Latn
- {0xCA630000u, 44u}, // dts -> Latn
- {0xE2630000u, 17u}, // dty -> Deva
- {0x82830000u, 44u}, // dua -> Latn
- {0x8A830000u, 44u}, // duc -> Latn
- {0x8E830000u, 44u}, // dud -> Latn
- {0x9A830000u, 44u}, // dug -> Latn
- {0x64760000u, 86u}, // dv -> Thaa
- {0x82A30000u, 44u}, // dva -> Latn
- {0xDAC30000u, 44u}, // dww -> Latn
- {0xBB030000u, 44u}, // dyo -> Latn
- {0xD3030000u, 44u}, // dyu -> Latn
- {0x647A0000u, 88u}, // dz -> Tibt
- {0x9B230000u, 44u}, // dzg -> Latn
- {0xD0240000u, 44u}, // ebu -> Latn
- {0x65650000u, 44u}, // ee -> Latn
- {0xA0A40000u, 44u}, // efi -> Latn
- {0xACC40000u, 44u}, // egl -> Latn
- {0xE0C40000u, 18u}, // egy -> Egyp
- {0x81440000u, 44u}, // eka -> Latn
- {0xE1440000u, 36u}, // eky -> Kali
- {0x656C0000u, 24u}, // el -> Grek
- {0x81840000u, 44u}, // ema -> Latn
- {0xA1840000u, 44u}, // emi -> Latn
- {0x656E0000u, 44u}, // en -> Latn
- {0x656E5841u, 95u}, // en-XA -> ~~~A
- {0xB5A40000u, 44u}, // enn -> Latn
- {0xC1A40000u, 44u}, // enq -> Latn
- {0x656F0000u, 44u}, // eo -> Latn
- {0xA2240000u, 44u}, // eri -> Latn
- {0x65730000u, 44u}, // es -> Latn
- {0x9A440000u, 22u}, // esg -> Gonm
- {0xD2440000u, 44u}, // esu -> Latn
- {0x65740000u, 44u}, // et -> Latn
- {0xC6640000u, 44u}, // etr -> Latn
- {0xCE640000u, 34u}, // ett -> Ital
- {0xD2640000u, 44u}, // etu -> Latn
- {0xDE640000u, 44u}, // etx -> Latn
- {0x65750000u, 44u}, // eu -> Latn
- {0xBAC40000u, 44u}, // ewo -> Latn
- {0xCEE40000u, 44u}, // ext -> Latn
+ {0xBDC30000u, 46u}, // dop -> Latn
+ {0xD9C30000u, 46u}, // dow -> Latn
+ {0x9E230000u, 56u}, // drh -> Mong
+ {0xA2230000u, 46u}, // dri -> Latn
+ {0xCA230000u, 20u}, // drs -> Ethi
+ {0x86430000u, 46u}, // dsb -> Latn
+ {0xB2630000u, 46u}, // dtm -> Latn
+ {0xBE630000u, 46u}, // dtp -> Latn
+ {0xCA630000u, 46u}, // dts -> Latn
+ {0xE2630000u, 18u}, // dty -> Deva
+ {0x82830000u, 46u}, // dua -> Latn
+ {0x8A830000u, 46u}, // duc -> Latn
+ {0x8E830000u, 46u}, // dud -> Latn
+ {0x9A830000u, 46u}, // dug -> Latn
+ {0x64760000u, 88u}, // dv -> Thaa
+ {0x82A30000u, 46u}, // dva -> Latn
+ {0xDAC30000u, 46u}, // dww -> Latn
+ {0xBB030000u, 46u}, // dyo -> Latn
+ {0xD3030000u, 46u}, // dyu -> Latn
+ {0x647A0000u, 90u}, // dz -> Tibt
+ {0x9B230000u, 46u}, // dzg -> Latn
+ {0xD0240000u, 46u}, // ebu -> Latn
+ {0x65650000u, 46u}, // ee -> Latn
+ {0xA0A40000u, 46u}, // efi -> Latn
+ {0xACC40000u, 46u}, // egl -> Latn
+ {0xE0C40000u, 19u}, // egy -> Egyp
+ {0x81440000u, 46u}, // eka -> Latn
+ {0xE1440000u, 37u}, // eky -> Kali
+ {0x656C0000u, 25u}, // el -> Grek
+ {0x81840000u, 46u}, // ema -> Latn
+ {0xA1840000u, 46u}, // emi -> Latn
+ {0x656E0000u, 46u}, // en -> Latn
+ {0x656E5841u, 97u}, // en-XA -> ~~~A
+ {0xB5A40000u, 46u}, // enn -> Latn
+ {0xC1A40000u, 46u}, // enq -> Latn
+ {0x656F0000u, 46u}, // eo -> Latn
+ {0xA2240000u, 46u}, // eri -> Latn
+ {0x65730000u, 46u}, // es -> Latn
+ {0x9A440000u, 23u}, // esg -> Gonm
+ {0xD2440000u, 46u}, // esu -> Latn
+ {0x65740000u, 46u}, // et -> Latn
+ {0xC6640000u, 46u}, // etr -> Latn
+ {0xCE640000u, 35u}, // ett -> Ital
+ {0xD2640000u, 46u}, // etu -> Latn
+ {0xDE640000u, 46u}, // etx -> Latn
+ {0x65750000u, 46u}, // eu -> Latn
+ {0xBAC40000u, 46u}, // ewo -> Latn
+ {0xCEE40000u, 46u}, // ext -> Latn
{0x66610000u, 1u}, // fa -> Arab
- {0x80050000u, 44u}, // faa -> Latn
- {0x84050000u, 44u}, // fab -> Latn
- {0x98050000u, 44u}, // fag -> Latn
- {0xA0050000u, 44u}, // fai -> Latn
- {0xB4050000u, 44u}, // fan -> Latn
- {0x66660000u, 44u}, // ff -> Latn
- {0xA0A50000u, 44u}, // ffi -> Latn
- {0xB0A50000u, 44u}, // ffm -> Latn
- {0x66690000u, 44u}, // fi -> Latn
+ {0x80050000u, 46u}, // faa -> Latn
+ {0x84050000u, 46u}, // fab -> Latn
+ {0x98050000u, 46u}, // fag -> Latn
+ {0xA0050000u, 46u}, // fai -> Latn
+ {0xB4050000u, 46u}, // fan -> Latn
+ {0x66660000u, 46u}, // ff -> Latn
+ {0xA0A50000u, 46u}, // ffi -> Latn
+ {0xB0A50000u, 46u}, // ffm -> Latn
+ {0x66690000u, 46u}, // fi -> Latn
{0x81050000u, 1u}, // fia -> Arab
- {0xAD050000u, 44u}, // fil -> Latn
- {0xCD050000u, 44u}, // fit -> Latn
- {0x666A0000u, 44u}, // fj -> Latn
- {0xC5650000u, 44u}, // flr -> Latn
- {0xBD850000u, 44u}, // fmp -> Latn
- {0x666F0000u, 44u}, // fo -> Latn
- {0x8DC50000u, 44u}, // fod -> Latn
- {0xB5C50000u, 44u}, // fon -> Latn
- {0xC5C50000u, 44u}, // for -> Latn
- {0x91E50000u, 44u}, // fpe -> Latn
- {0xCA050000u, 44u}, // fqs -> Latn
- {0x66720000u, 44u}, // fr -> Latn
- {0x8A250000u, 44u}, // frc -> Latn
- {0xBE250000u, 44u}, // frp -> Latn
- {0xC6250000u, 44u}, // frr -> Latn
- {0xCA250000u, 44u}, // frs -> Latn
+ {0xAD050000u, 46u}, // fil -> Latn
+ {0xCD050000u, 46u}, // fit -> Latn
+ {0x666A0000u, 46u}, // fj -> Latn
+ {0xC5650000u, 46u}, // flr -> Latn
+ {0xBD850000u, 46u}, // fmp -> Latn
+ {0x666F0000u, 46u}, // fo -> Latn
+ {0x8DC50000u, 46u}, // fod -> Latn
+ {0xB5C50000u, 46u}, // fon -> Latn
+ {0xC5C50000u, 46u}, // for -> Latn
+ {0x91E50000u, 46u}, // fpe -> Latn
+ {0xCA050000u, 46u}, // fqs -> Latn
+ {0x66720000u, 46u}, // fr -> Latn
+ {0x8A250000u, 46u}, // frc -> Latn
+ {0xBE250000u, 46u}, // frp -> Latn
+ {0xC6250000u, 46u}, // frr -> Latn
+ {0xCA250000u, 46u}, // frs -> Latn
{0x86850000u, 1u}, // fub -> Arab
- {0x8E850000u, 44u}, // fud -> Latn
- {0x92850000u, 44u}, // fue -> Latn
- {0x96850000u, 44u}, // fuf -> Latn
- {0x9E850000u, 44u}, // fuh -> Latn
- {0xC2850000u, 44u}, // fuq -> Latn
- {0xC6850000u, 44u}, // fur -> Latn
- {0xD6850000u, 44u}, // fuv -> Latn
- {0xE2850000u, 44u}, // fuy -> Latn
- {0xC6A50000u, 44u}, // fvr -> Latn
- {0x66790000u, 44u}, // fy -> Latn
- {0x67610000u, 44u}, // ga -> Latn
- {0x80060000u, 44u}, // gaa -> Latn
- {0x94060000u, 44u}, // gaf -> Latn
- {0x98060000u, 44u}, // gag -> Latn
- {0x9C060000u, 44u}, // gah -> Latn
- {0xA4060000u, 44u}, // gaj -> Latn
- {0xB0060000u, 44u}, // gam -> Latn
- {0xB4060000u, 27u}, // gan -> Hans
- {0xD8060000u, 44u}, // gaw -> Latn
- {0xE0060000u, 44u}, // gay -> Latn
- {0x80260000u, 44u}, // gba -> Latn
- {0x94260000u, 44u}, // gbf -> Latn
- {0xB0260000u, 17u}, // gbm -> Deva
- {0xE0260000u, 44u}, // gby -> Latn
+ {0x8E850000u, 46u}, // fud -> Latn
+ {0x92850000u, 46u}, // fue -> Latn
+ {0x96850000u, 46u}, // fuf -> Latn
+ {0x9E850000u, 46u}, // fuh -> Latn
+ {0xC2850000u, 46u}, // fuq -> Latn
+ {0xC6850000u, 46u}, // fur -> Latn
+ {0xD6850000u, 46u}, // fuv -> Latn
+ {0xE2850000u, 46u}, // fuy -> Latn
+ {0xC6A50000u, 46u}, // fvr -> Latn
+ {0x66790000u, 46u}, // fy -> Latn
+ {0x67610000u, 46u}, // ga -> Latn
+ {0x80060000u, 46u}, // gaa -> Latn
+ {0x94060000u, 46u}, // gaf -> Latn
+ {0x98060000u, 46u}, // gag -> Latn
+ {0x9C060000u, 46u}, // gah -> Latn
+ {0xA4060000u, 46u}, // gaj -> Latn
+ {0xB0060000u, 46u}, // gam -> Latn
+ {0xB4060000u, 28u}, // gan -> Hans
+ {0xD8060000u, 46u}, // gaw -> Latn
+ {0xE0060000u, 46u}, // gay -> Latn
+ {0x80260000u, 46u}, // gba -> Latn
+ {0x94260000u, 46u}, // gbf -> Latn
+ {0xB0260000u, 18u}, // gbm -> Deva
+ {0xE0260000u, 46u}, // gby -> Latn
{0xE4260000u, 1u}, // gbz -> Arab
- {0xC4460000u, 44u}, // gcr -> Latn
- {0x67640000u, 44u}, // gd -> Latn
- {0x90660000u, 44u}, // gde -> Latn
- {0xB4660000u, 44u}, // gdn -> Latn
- {0xC4660000u, 44u}, // gdr -> Latn
- {0x84860000u, 44u}, // geb -> Latn
- {0xA4860000u, 44u}, // gej -> Latn
- {0xAC860000u, 44u}, // gel -> Latn
- {0xE4860000u, 19u}, // gez -> Ethi
- {0xA8A60000u, 44u}, // gfk -> Latn
- {0xB4C60000u, 17u}, // ggn -> Deva
- {0xC8E60000u, 44u}, // ghs -> Latn
- {0xAD060000u, 44u}, // gil -> Latn
- {0xB1060000u, 44u}, // gim -> Latn
+ {0xC4460000u, 46u}, // gcr -> Latn
+ {0x67640000u, 46u}, // gd -> Latn
+ {0x90660000u, 46u}, // gde -> Latn
+ {0xB4660000u, 46u}, // gdn -> Latn
+ {0xC4660000u, 46u}, // gdr -> Latn
+ {0x84860000u, 46u}, // geb -> Latn
+ {0xA4860000u, 46u}, // gej -> Latn
+ {0xAC860000u, 46u}, // gel -> Latn
+ {0xE4860000u, 20u}, // gez -> Ethi
+ {0xA8A60000u, 46u}, // gfk -> Latn
+ {0xB4C60000u, 18u}, // ggn -> Deva
+ {0xC8E60000u, 46u}, // ghs -> Latn
+ {0xAD060000u, 46u}, // gil -> Latn
+ {0xB1060000u, 46u}, // gim -> Latn
{0xA9260000u, 1u}, // gjk -> Arab
- {0xB5260000u, 44u}, // gjn -> Latn
+ {0xB5260000u, 46u}, // gjn -> Latn
{0xD1260000u, 1u}, // gju -> Arab
- {0xB5460000u, 44u}, // gkn -> Latn
- {0xBD460000u, 44u}, // gkp -> Latn
- {0x676C0000u, 44u}, // gl -> Latn
+ {0xB5460000u, 46u}, // gkn -> Latn
+ {0xBD460000u, 46u}, // gkp -> Latn
+ {0x676C0000u, 46u}, // gl -> Latn
{0xA9660000u, 1u}, // glk -> Arab
- {0xB1860000u, 44u}, // gmm -> Latn
- {0xD5860000u, 19u}, // gmv -> Ethi
- {0x676E0000u, 44u}, // gn -> Latn
- {0x8DA60000u, 44u}, // gnd -> Latn
- {0x99A60000u, 44u}, // gng -> Latn
- {0x8DC60000u, 44u}, // god -> Latn
- {0x95C60000u, 19u}, // gof -> Ethi
- {0xA1C60000u, 44u}, // goi -> Latn
- {0xB1C60000u, 17u}, // gom -> Deva
- {0xB5C60000u, 84u}, // gon -> Telu
- {0xC5C60000u, 44u}, // gor -> Latn
- {0xC9C60000u, 44u}, // gos -> Latn
- {0xCDC60000u, 23u}, // got -> Goth
- {0x86260000u, 44u}, // grb -> Latn
- {0x8A260000u, 15u}, // grc -> Cprt
+ {0xB1860000u, 46u}, // gmm -> Latn
+ {0xD5860000u, 20u}, // gmv -> Ethi
+ {0x676E0000u, 46u}, // gn -> Latn
+ {0x8DA60000u, 46u}, // gnd -> Latn
+ {0x99A60000u, 46u}, // gng -> Latn
+ {0x8DC60000u, 46u}, // god -> Latn
+ {0x95C60000u, 20u}, // gof -> Ethi
+ {0xA1C60000u, 46u}, // goi -> Latn
+ {0xB1C60000u, 18u}, // gom -> Deva
+ {0xB5C60000u, 86u}, // gon -> Telu
+ {0xC5C60000u, 46u}, // gor -> Latn
+ {0xC9C60000u, 46u}, // gos -> Latn
+ {0xCDC60000u, 24u}, // got -> Goth
+ {0x86260000u, 46u}, // grb -> Latn
+ {0x8A260000u, 16u}, // grc -> Cprt
{0xCE260000u, 7u}, // grt -> Beng
- {0xDA260000u, 44u}, // grw -> Latn
- {0xDA460000u, 44u}, // gsw -> Latn
- {0x67750000u, 25u}, // gu -> Gujr
- {0x86860000u, 44u}, // gub -> Latn
- {0x8A860000u, 44u}, // guc -> Latn
- {0x8E860000u, 44u}, // gud -> Latn
- {0xC6860000u, 44u}, // gur -> Latn
- {0xDA860000u, 44u}, // guw -> Latn
- {0xDE860000u, 44u}, // gux -> Latn
- {0xE6860000u, 44u}, // guz -> Latn
- {0x67760000u, 44u}, // gv -> Latn
- {0x96A60000u, 44u}, // gvf -> Latn
- {0xC6A60000u, 17u}, // gvr -> Deva
- {0xCAA60000u, 44u}, // gvs -> Latn
+ {0xDA260000u, 46u}, // grw -> Latn
+ {0xDA460000u, 46u}, // gsw -> Latn
+ {0x67750000u, 26u}, // gu -> Gujr
+ {0x86860000u, 46u}, // gub -> Latn
+ {0x8A860000u, 46u}, // guc -> Latn
+ {0x8E860000u, 46u}, // gud -> Latn
+ {0xC6860000u, 46u}, // gur -> Latn
+ {0xDA860000u, 46u}, // guw -> Latn
+ {0xDE860000u, 46u}, // gux -> Latn
+ {0xE6860000u, 46u}, // guz -> Latn
+ {0x67760000u, 46u}, // gv -> Latn
+ {0x96A60000u, 46u}, // gvf -> Latn
+ {0xC6A60000u, 18u}, // gvr -> Deva
+ {0xCAA60000u, 46u}, // gvs -> Latn
{0x8AC60000u, 1u}, // gwc -> Arab
- {0xA2C60000u, 44u}, // gwi -> Latn
+ {0xA2C60000u, 46u}, // gwi -> Latn
{0xCEC60000u, 1u}, // gwt -> Arab
- {0xA3060000u, 44u}, // gyi -> Latn
- {0x68610000u, 44u}, // ha -> Latn
+ {0xA3060000u, 46u}, // gyi -> Latn
+ {0x68610000u, 46u}, // ha -> Latn
{0x6861434Du, 1u}, // ha-CM -> Arab
{0x68615344u, 1u}, // ha-SD -> Arab
- {0x98070000u, 44u}, // hag -> Latn
- {0xA8070000u, 27u}, // hak -> Hans
- {0xB0070000u, 44u}, // ham -> Latn
- {0xD8070000u, 44u}, // haw -> Latn
+ {0x98070000u, 46u}, // hag -> Latn
+ {0xA8070000u, 28u}, // hak -> Hans
+ {0xB0070000u, 46u}, // ham -> Latn
+ {0xD8070000u, 46u}, // haw -> Latn
{0xE4070000u, 1u}, // haz -> Arab
- {0x84270000u, 44u}, // hbb -> Latn
- {0xE0670000u, 19u}, // hdy -> Ethi
- {0x68650000u, 30u}, // he -> Hebr
- {0xE0E70000u, 44u}, // hhy -> Latn
- {0x68690000u, 17u}, // hi -> Deva
- {0x81070000u, 44u}, // hia -> Latn
- {0x95070000u, 44u}, // hif -> Latn
- {0x99070000u, 44u}, // hig -> Latn
- {0x9D070000u, 44u}, // hih -> Latn
- {0xAD070000u, 44u}, // hil -> Latn
- {0x81670000u, 44u}, // hla -> Latn
- {0xD1670000u, 31u}, // hlu -> Hluw
- {0x8D870000u, 67u}, // hmd -> Plrd
- {0xCD870000u, 44u}, // hmt -> Latn
+ {0x84270000u, 46u}, // hbb -> Latn
+ {0xE0670000u, 20u}, // hdy -> Ethi
+ {0x68650000u, 31u}, // he -> Hebr
+ {0xE0E70000u, 46u}, // hhy -> Latn
+ {0x68690000u, 18u}, // hi -> Deva
+ {0x81070000u, 46u}, // hia -> Latn
+ {0x95070000u, 46u}, // hif -> Latn
+ {0x99070000u, 46u}, // hig -> Latn
+ {0x9D070000u, 46u}, // hih -> Latn
+ {0xAD070000u, 46u}, // hil -> Latn
+ {0x81670000u, 46u}, // hla -> Latn
+ {0xD1670000u, 32u}, // hlu -> Hluw
+ {0x8D870000u, 69u}, // hmd -> Plrd
+ {0xCD870000u, 46u}, // hmt -> Latn
{0x8DA70000u, 1u}, // hnd -> Arab
- {0x91A70000u, 17u}, // hne -> Deva
- {0xA5A70000u, 32u}, // hnj -> Hmng
- {0xB5A70000u, 44u}, // hnn -> Latn
+ {0x91A70000u, 18u}, // hne -> Deva
+ {0xA5A70000u, 33u}, // hnj -> Hmng
+ {0xB5A70000u, 46u}, // hnn -> Latn
{0xB9A70000u, 1u}, // hno -> Arab
- {0x686F0000u, 44u}, // ho -> Latn
- {0x89C70000u, 17u}, // hoc -> Deva
- {0xA5C70000u, 17u}, // hoj -> Deva
- {0xCDC70000u, 44u}, // hot -> Latn
- {0x68720000u, 44u}, // hr -> Latn
- {0x86470000u, 44u}, // hsb -> Latn
- {0xB6470000u, 27u}, // hsn -> Hans
- {0x68740000u, 44u}, // ht -> Latn
- {0x68750000u, 44u}, // hu -> Latn
- {0xA2870000u, 44u}, // hui -> Latn
+ {0x686F0000u, 46u}, // ho -> Latn
+ {0x89C70000u, 18u}, // hoc -> Deva
+ {0xA5C70000u, 18u}, // hoj -> Deva
+ {0xCDC70000u, 46u}, // hot -> Latn
+ {0x68720000u, 46u}, // hr -> Latn
+ {0x86470000u, 46u}, // hsb -> Latn
+ {0xB6470000u, 28u}, // hsn -> Hans
+ {0x68740000u, 46u}, // ht -> Latn
+ {0x68750000u, 46u}, // hu -> Latn
+ {0xA2870000u, 46u}, // hui -> Latn
{0x68790000u, 3u}, // hy -> Armn
- {0x687A0000u, 44u}, // hz -> Latn
- {0x69610000u, 44u}, // ia -> Latn
- {0xB4080000u, 44u}, // ian -> Latn
- {0xC4080000u, 44u}, // iar -> Latn
- {0x80280000u, 44u}, // iba -> Latn
- {0x84280000u, 44u}, // ibb -> Latn
- {0xE0280000u, 44u}, // iby -> Latn
- {0x80480000u, 44u}, // ica -> Latn
- {0x9C480000u, 44u}, // ich -> Latn
- {0x69640000u, 44u}, // id -> Latn
- {0x8C680000u, 44u}, // idd -> Latn
- {0xA0680000u, 44u}, // idi -> Latn
- {0xD0680000u, 44u}, // idu -> Latn
- {0x90A80000u, 44u}, // ife -> Latn
- {0x69670000u, 44u}, // ig -> Latn
- {0x84C80000u, 44u}, // igb -> Latn
- {0x90C80000u, 44u}, // ige -> Latn
- {0x69690000u, 94u}, // ii -> Yiii
- {0xA5280000u, 44u}, // ijj -> Latn
- {0x696B0000u, 44u}, // ik -> Latn
- {0xA9480000u, 44u}, // ikk -> Latn
- {0xCD480000u, 44u}, // ikt -> Latn
- {0xD9480000u, 44u}, // ikw -> Latn
- {0xDD480000u, 44u}, // ikx -> Latn
- {0xB9680000u, 44u}, // ilo -> Latn
- {0xB9880000u, 44u}, // imo -> Latn
- {0x696E0000u, 44u}, // in -> Latn
- {0x9DA80000u, 16u}, // inh -> Cyrl
- {0x696F0000u, 44u}, // io -> Latn
- {0xD1C80000u, 44u}, // iou -> Latn
- {0xA2280000u, 44u}, // iri -> Latn
- {0x69730000u, 44u}, // is -> Latn
- {0x69740000u, 44u}, // it -> Latn
+ {0x687A0000u, 46u}, // hz -> Latn
+ {0x69610000u, 46u}, // ia -> Latn
+ {0xB4080000u, 46u}, // ian -> Latn
+ {0xC4080000u, 46u}, // iar -> Latn
+ {0x80280000u, 46u}, // iba -> Latn
+ {0x84280000u, 46u}, // ibb -> Latn
+ {0xE0280000u, 46u}, // iby -> Latn
+ {0x80480000u, 46u}, // ica -> Latn
+ {0x9C480000u, 46u}, // ich -> Latn
+ {0x69640000u, 46u}, // id -> Latn
+ {0x8C680000u, 46u}, // idd -> Latn
+ {0xA0680000u, 46u}, // idi -> Latn
+ {0xD0680000u, 46u}, // idu -> Latn
+ {0x90A80000u, 46u}, // ife -> Latn
+ {0x69670000u, 46u}, // ig -> Latn
+ {0x84C80000u, 46u}, // igb -> Latn
+ {0x90C80000u, 46u}, // ige -> Latn
+ {0x69690000u, 96u}, // ii -> Yiii
+ {0xA5280000u, 46u}, // ijj -> Latn
+ {0x696B0000u, 46u}, // ik -> Latn
+ {0xA9480000u, 46u}, // ikk -> Latn
+ {0xCD480000u, 46u}, // ikt -> Latn
+ {0xD9480000u, 46u}, // ikw -> Latn
+ {0xDD480000u, 46u}, // ikx -> Latn
+ {0xB9680000u, 46u}, // ilo -> Latn
+ {0xB9880000u, 46u}, // imo -> Latn
+ {0x696E0000u, 46u}, // in -> Latn
+ {0x9DA80000u, 17u}, // inh -> Cyrl
+ {0x696F0000u, 46u}, // io -> Latn
+ {0xD1C80000u, 46u}, // iou -> Latn
+ {0xA2280000u, 46u}, // iri -> Latn
+ {0x69730000u, 46u}, // is -> Latn
+ {0x69740000u, 46u}, // it -> Latn
{0x69750000u, 10u}, // iu -> Cans
- {0x69770000u, 30u}, // iw -> Hebr
- {0xB2C80000u, 44u}, // iwm -> Latn
- {0xCAC80000u, 44u}, // iws -> Latn
- {0x9F280000u, 44u}, // izh -> Latn
- {0xA3280000u, 44u}, // izi -> Latn
- {0x6A610000u, 35u}, // ja -> Jpan
- {0x84090000u, 44u}, // jab -> Latn
- {0xB0090000u, 44u}, // jam -> Latn
- {0xB8290000u, 44u}, // jbo -> Latn
- {0xD0290000u, 44u}, // jbu -> Latn
- {0xB4890000u, 44u}, // jen -> Latn
- {0xA8C90000u, 44u}, // jgk -> Latn
- {0xB8C90000u, 44u}, // jgo -> Latn
- {0x6A690000u, 30u}, // ji -> Hebr
- {0x85090000u, 44u}, // jib -> Latn
- {0x89890000u, 44u}, // jmc -> Latn
- {0xAD890000u, 17u}, // jml -> Deva
- {0x82290000u, 44u}, // jra -> Latn
- {0xCE890000u, 44u}, // jut -> Latn
- {0x6A760000u, 44u}, // jv -> Latn
- {0x6A770000u, 44u}, // jw -> Latn
- {0x6B610000u, 20u}, // ka -> Geor
- {0x800A0000u, 16u}, // kaa -> Cyrl
- {0x840A0000u, 44u}, // kab -> Latn
- {0x880A0000u, 44u}, // kac -> Latn
- {0x8C0A0000u, 44u}, // kad -> Latn
- {0xA00A0000u, 44u}, // kai -> Latn
- {0xA40A0000u, 44u}, // kaj -> Latn
- {0xB00A0000u, 44u}, // kam -> Latn
- {0xB80A0000u, 44u}, // kao -> Latn
- {0x8C2A0000u, 16u}, // kbd -> Cyrl
- {0xB02A0000u, 44u}, // kbm -> Latn
- {0xBC2A0000u, 44u}, // kbp -> Latn
- {0xC02A0000u, 44u}, // kbq -> Latn
- {0xDC2A0000u, 44u}, // kbx -> Latn
+ {0x69770000u, 31u}, // iw -> Hebr
+ {0xB2C80000u, 46u}, // iwm -> Latn
+ {0xCAC80000u, 46u}, // iws -> Latn
+ {0x9F280000u, 46u}, // izh -> Latn
+ {0xA3280000u, 46u}, // izi -> Latn
+ {0x6A610000u, 36u}, // ja -> Jpan
+ {0x84090000u, 46u}, // jab -> Latn
+ {0xB0090000u, 46u}, // jam -> Latn
+ {0xB8290000u, 46u}, // jbo -> Latn
+ {0xD0290000u, 46u}, // jbu -> Latn
+ {0xB4890000u, 46u}, // jen -> Latn
+ {0xA8C90000u, 46u}, // jgk -> Latn
+ {0xB8C90000u, 46u}, // jgo -> Latn
+ {0x6A690000u, 31u}, // ji -> Hebr
+ {0x85090000u, 46u}, // jib -> Latn
+ {0x89890000u, 46u}, // jmc -> Latn
+ {0xAD890000u, 18u}, // jml -> Deva
+ {0x82290000u, 46u}, // jra -> Latn
+ {0xCE890000u, 46u}, // jut -> Latn
+ {0x6A760000u, 46u}, // jv -> Latn
+ {0x6A770000u, 46u}, // jw -> Latn
+ {0x6B610000u, 21u}, // ka -> Geor
+ {0x800A0000u, 17u}, // kaa -> Cyrl
+ {0x840A0000u, 46u}, // kab -> Latn
+ {0x880A0000u, 46u}, // kac -> Latn
+ {0x8C0A0000u, 46u}, // kad -> Latn
+ {0xA00A0000u, 46u}, // kai -> Latn
+ {0xA40A0000u, 46u}, // kaj -> Latn
+ {0xB00A0000u, 46u}, // kam -> Latn
+ {0xB80A0000u, 46u}, // kao -> Latn
+ {0x8C2A0000u, 17u}, // kbd -> Cyrl
+ {0xB02A0000u, 46u}, // kbm -> Latn
+ {0xBC2A0000u, 46u}, // kbp -> Latn
+ {0xC02A0000u, 46u}, // kbq -> Latn
+ {0xDC2A0000u, 46u}, // kbx -> Latn
{0xE02A0000u, 1u}, // kby -> Arab
- {0x984A0000u, 44u}, // kcg -> Latn
- {0xA84A0000u, 44u}, // kck -> Latn
- {0xAC4A0000u, 44u}, // kcl -> Latn
- {0xCC4A0000u, 44u}, // kct -> Latn
- {0x906A0000u, 44u}, // kde -> Latn
+ {0x984A0000u, 46u}, // kcg -> Latn
+ {0xA84A0000u, 46u}, // kck -> Latn
+ {0xAC4A0000u, 46u}, // kcl -> Latn
+ {0xCC4A0000u, 46u}, // kct -> Latn
+ {0x906A0000u, 46u}, // kde -> Latn
{0x9C6A0000u, 1u}, // kdh -> Arab
- {0xAC6A0000u, 44u}, // kdl -> Latn
- {0xCC6A0000u, 87u}, // kdt -> Thai
- {0x808A0000u, 44u}, // kea -> Latn
- {0xB48A0000u, 44u}, // ken -> Latn
- {0xE48A0000u, 44u}, // kez -> Latn
- {0xB8AA0000u, 44u}, // kfo -> Latn
- {0xC4AA0000u, 17u}, // kfr -> Deva
- {0xE0AA0000u, 17u}, // kfy -> Deva
- {0x6B670000u, 44u}, // kg -> Latn
- {0x90CA0000u, 44u}, // kge -> Latn
- {0x94CA0000u, 44u}, // kgf -> Latn
- {0xBCCA0000u, 44u}, // kgp -> Latn
- {0x80EA0000u, 44u}, // kha -> Latn
- {0x84EA0000u, 80u}, // khb -> Talu
- {0xB4EA0000u, 17u}, // khn -> Deva
- {0xC0EA0000u, 44u}, // khq -> Latn
- {0xC8EA0000u, 44u}, // khs -> Latn
- {0xCCEA0000u, 56u}, // kht -> Mymr
+ {0xAC6A0000u, 46u}, // kdl -> Latn
+ {0xCC6A0000u, 89u}, // kdt -> Thai
+ {0x808A0000u, 46u}, // kea -> Latn
+ {0xB48A0000u, 46u}, // ken -> Latn
+ {0xE48A0000u, 46u}, // kez -> Latn
+ {0xB8AA0000u, 46u}, // kfo -> Latn
+ {0xC4AA0000u, 18u}, // kfr -> Deva
+ {0xE0AA0000u, 18u}, // kfy -> Deva
+ {0x6B670000u, 46u}, // kg -> Latn
+ {0x90CA0000u, 46u}, // kge -> Latn
+ {0x94CA0000u, 46u}, // kgf -> Latn
+ {0xBCCA0000u, 46u}, // kgp -> Latn
+ {0x80EA0000u, 46u}, // kha -> Latn
+ {0x84EA0000u, 82u}, // khb -> Talu
+ {0xB4EA0000u, 18u}, // khn -> Deva
+ {0xC0EA0000u, 46u}, // khq -> Latn
+ {0xC8EA0000u, 46u}, // khs -> Latn
+ {0xCCEA0000u, 58u}, // kht -> Mymr
{0xD8EA0000u, 1u}, // khw -> Arab
- {0xE4EA0000u, 44u}, // khz -> Latn
- {0x6B690000u, 44u}, // ki -> Latn
- {0xA50A0000u, 44u}, // kij -> Latn
- {0xD10A0000u, 44u}, // kiu -> Latn
- {0xD90A0000u, 44u}, // kiw -> Latn
- {0x6B6A0000u, 44u}, // kj -> Latn
- {0x8D2A0000u, 44u}, // kjd -> Latn
- {0x992A0000u, 43u}, // kjg -> Laoo
- {0xC92A0000u, 44u}, // kjs -> Latn
- {0xE12A0000u, 44u}, // kjy -> Latn
- {0x6B6B0000u, 16u}, // kk -> Cyrl
+ {0xE4EA0000u, 46u}, // khz -> Latn
+ {0x6B690000u, 46u}, // ki -> Latn
+ {0xA50A0000u, 46u}, // kij -> Latn
+ {0xD10A0000u, 46u}, // kiu -> Latn
+ {0xD90A0000u, 46u}, // kiw -> Latn
+ {0x6B6A0000u, 46u}, // kj -> Latn
+ {0x8D2A0000u, 46u}, // kjd -> Latn
+ {0x992A0000u, 45u}, // kjg -> Laoo
+ {0xC92A0000u, 46u}, // kjs -> Latn
+ {0xE12A0000u, 46u}, // kjy -> Latn
+ {0x6B6B0000u, 17u}, // kk -> Cyrl
{0x6B6B4146u, 1u}, // kk-AF -> Arab
{0x6B6B434Eu, 1u}, // kk-CN -> Arab
{0x6B6B4952u, 1u}, // kk-IR -> Arab
{0x6B6B4D4Eu, 1u}, // kk-MN -> Arab
- {0x894A0000u, 44u}, // kkc -> Latn
- {0xA54A0000u, 44u}, // kkj -> Latn
- {0x6B6C0000u, 44u}, // kl -> Latn
- {0xB56A0000u, 44u}, // kln -> Latn
- {0xC16A0000u, 44u}, // klq -> Latn
- {0xCD6A0000u, 44u}, // klt -> Latn
- {0xDD6A0000u, 44u}, // klx -> Latn
- {0x6B6D0000u, 39u}, // km -> Khmr
- {0x858A0000u, 44u}, // kmb -> Latn
- {0x9D8A0000u, 44u}, // kmh -> Latn
- {0xB98A0000u, 44u}, // kmo -> Latn
- {0xC98A0000u, 44u}, // kms -> Latn
- {0xD18A0000u, 44u}, // kmu -> Latn
- {0xD98A0000u, 44u}, // kmw -> Latn
- {0x6B6E0000u, 40u}, // kn -> Knda
- {0x95AA0000u, 44u}, // knf -> Latn
- {0xBDAA0000u, 44u}, // knp -> Latn
- {0x6B6F0000u, 41u}, // ko -> Kore
- {0xA1CA0000u, 16u}, // koi -> Cyrl
- {0xA9CA0000u, 17u}, // kok -> Deva
- {0xADCA0000u, 44u}, // kol -> Latn
- {0xC9CA0000u, 44u}, // kos -> Latn
- {0xE5CA0000u, 44u}, // koz -> Latn
- {0x91EA0000u, 44u}, // kpe -> Latn
- {0x95EA0000u, 44u}, // kpf -> Latn
- {0xB9EA0000u, 44u}, // kpo -> Latn
- {0xC5EA0000u, 44u}, // kpr -> Latn
- {0xDDEA0000u, 44u}, // kpx -> Latn
- {0x860A0000u, 44u}, // kqb -> Latn
- {0x960A0000u, 44u}, // kqf -> Latn
- {0xCA0A0000u, 44u}, // kqs -> Latn
- {0xE20A0000u, 19u}, // kqy -> Ethi
- {0x6B720000u, 44u}, // kr -> Latn
- {0x8A2A0000u, 16u}, // krc -> Cyrl
- {0xA22A0000u, 44u}, // kri -> Latn
- {0xA62A0000u, 44u}, // krj -> Latn
- {0xAE2A0000u, 44u}, // krl -> Latn
- {0xCA2A0000u, 44u}, // krs -> Latn
- {0xD22A0000u, 17u}, // kru -> Deva
+ {0x894A0000u, 46u}, // kkc -> Latn
+ {0xA54A0000u, 46u}, // kkj -> Latn
+ {0x6B6C0000u, 46u}, // kl -> Latn
+ {0xB56A0000u, 46u}, // kln -> Latn
+ {0xC16A0000u, 46u}, // klq -> Latn
+ {0xCD6A0000u, 46u}, // klt -> Latn
+ {0xDD6A0000u, 46u}, // klx -> Latn
+ {0x6B6D0000u, 40u}, // km -> Khmr
+ {0x858A0000u, 46u}, // kmb -> Latn
+ {0x9D8A0000u, 46u}, // kmh -> Latn
+ {0xB98A0000u, 46u}, // kmo -> Latn
+ {0xC98A0000u, 46u}, // kms -> Latn
+ {0xD18A0000u, 46u}, // kmu -> Latn
+ {0xD98A0000u, 46u}, // kmw -> Latn
+ {0x6B6E0000u, 42u}, // kn -> Knda
+ {0x95AA0000u, 46u}, // knf -> Latn
+ {0xBDAA0000u, 46u}, // knp -> Latn
+ {0x6B6F0000u, 43u}, // ko -> Kore
+ {0xA1CA0000u, 17u}, // koi -> Cyrl
+ {0xA9CA0000u, 18u}, // kok -> Deva
+ {0xADCA0000u, 46u}, // kol -> Latn
+ {0xC9CA0000u, 46u}, // kos -> Latn
+ {0xE5CA0000u, 46u}, // koz -> Latn
+ {0x91EA0000u, 46u}, // kpe -> Latn
+ {0x95EA0000u, 46u}, // kpf -> Latn
+ {0xB9EA0000u, 46u}, // kpo -> Latn
+ {0xC5EA0000u, 46u}, // kpr -> Latn
+ {0xDDEA0000u, 46u}, // kpx -> Latn
+ {0x860A0000u, 46u}, // kqb -> Latn
+ {0x960A0000u, 46u}, // kqf -> Latn
+ {0xCA0A0000u, 46u}, // kqs -> Latn
+ {0xE20A0000u, 20u}, // kqy -> Ethi
+ {0x6B720000u, 46u}, // kr -> Latn
+ {0x8A2A0000u, 17u}, // krc -> Cyrl
+ {0xA22A0000u, 46u}, // kri -> Latn
+ {0xA62A0000u, 46u}, // krj -> Latn
+ {0xAE2A0000u, 46u}, // krl -> Latn
+ {0xCA2A0000u, 46u}, // krs -> Latn
+ {0xD22A0000u, 18u}, // kru -> Deva
{0x6B730000u, 1u}, // ks -> Arab
- {0x864A0000u, 44u}, // ksb -> Latn
- {0x8E4A0000u, 44u}, // ksd -> Latn
- {0x964A0000u, 44u}, // ksf -> Latn
- {0x9E4A0000u, 44u}, // ksh -> Latn
- {0xA64A0000u, 44u}, // ksj -> Latn
- {0xC64A0000u, 44u}, // ksr -> Latn
- {0x866A0000u, 19u}, // ktb -> Ethi
- {0xB26A0000u, 44u}, // ktm -> Latn
- {0xBA6A0000u, 44u}, // kto -> Latn
- {0xC66A0000u, 44u}, // ktr -> Latn
- {0x6B750000u, 44u}, // ku -> Latn
+ {0x864A0000u, 46u}, // ksb -> Latn
+ {0x8E4A0000u, 46u}, // ksd -> Latn
+ {0x964A0000u, 46u}, // ksf -> Latn
+ {0x9E4A0000u, 46u}, // ksh -> Latn
+ {0xA64A0000u, 46u}, // ksj -> Latn
+ {0xC64A0000u, 46u}, // ksr -> Latn
+ {0x866A0000u, 20u}, // ktb -> Ethi
+ {0xB26A0000u, 46u}, // ktm -> Latn
+ {0xBA6A0000u, 46u}, // kto -> Latn
+ {0xC66A0000u, 46u}, // ktr -> Latn
+ {0x6B750000u, 46u}, // ku -> Latn
{0x6B754952u, 1u}, // ku-IR -> Arab
{0x6B754C42u, 1u}, // ku-LB -> Arab
- {0x868A0000u, 44u}, // kub -> Latn
- {0x8E8A0000u, 44u}, // kud -> Latn
- {0x928A0000u, 44u}, // kue -> Latn
- {0xA68A0000u, 44u}, // kuj -> Latn
- {0xB28A0000u, 16u}, // kum -> Cyrl
- {0xB68A0000u, 44u}, // kun -> Latn
- {0xBE8A0000u, 44u}, // kup -> Latn
- {0xCA8A0000u, 44u}, // kus -> Latn
- {0x6B760000u, 16u}, // kv -> Cyrl
- {0x9AAA0000u, 44u}, // kvg -> Latn
- {0xC6AA0000u, 44u}, // kvr -> Latn
+ {0x868A0000u, 46u}, // kub -> Latn
+ {0x8E8A0000u, 46u}, // kud -> Latn
+ {0x928A0000u, 46u}, // kue -> Latn
+ {0xA68A0000u, 46u}, // kuj -> Latn
+ {0xB28A0000u, 17u}, // kum -> Cyrl
+ {0xB68A0000u, 46u}, // kun -> Latn
+ {0xBE8A0000u, 46u}, // kup -> Latn
+ {0xCA8A0000u, 46u}, // kus -> Latn
+ {0x6B760000u, 17u}, // kv -> Cyrl
+ {0x9AAA0000u, 46u}, // kvg -> Latn
+ {0xC6AA0000u, 46u}, // kvr -> Latn
{0xDEAA0000u, 1u}, // kvx -> Arab
- {0x6B770000u, 44u}, // kw -> Latn
- {0xA6CA0000u, 44u}, // kwj -> Latn
- {0xBACA0000u, 44u}, // kwo -> Latn
- {0xC2CA0000u, 44u}, // kwq -> Latn
- {0x82EA0000u, 44u}, // kxa -> Latn
- {0x8AEA0000u, 19u}, // kxc -> Ethi
- {0x92EA0000u, 44u}, // kxe -> Latn
- {0xB2EA0000u, 87u}, // kxm -> Thai
+ {0x6B770000u, 46u}, // kw -> Latn
+ {0xA6CA0000u, 46u}, // kwj -> Latn
+ {0xBACA0000u, 46u}, // kwo -> Latn
+ {0xC2CA0000u, 46u}, // kwq -> Latn
+ {0x82EA0000u, 46u}, // kxa -> Latn
+ {0x8AEA0000u, 20u}, // kxc -> Ethi
+ {0x92EA0000u, 46u}, // kxe -> Latn
+ {0xB2EA0000u, 89u}, // kxm -> Thai
{0xBEEA0000u, 1u}, // kxp -> Arab
- {0xDAEA0000u, 44u}, // kxw -> Latn
- {0xE6EA0000u, 44u}, // kxz -> Latn
- {0x6B790000u, 16u}, // ky -> Cyrl
+ {0xDAEA0000u, 46u}, // kxw -> Latn
+ {0xE6EA0000u, 46u}, // kxz -> Latn
+ {0x6B790000u, 17u}, // ky -> Cyrl
{0x6B79434Eu, 1u}, // ky-CN -> Arab
- {0x6B795452u, 44u}, // ky-TR -> Latn
- {0x930A0000u, 44u}, // kye -> Latn
- {0xDF0A0000u, 44u}, // kyx -> Latn
- {0xA72A0000u, 44u}, // kzj -> Latn
- {0xC72A0000u, 44u}, // kzr -> Latn
- {0xCF2A0000u, 44u}, // kzt -> Latn
- {0x6C610000u, 44u}, // la -> Latn
- {0x840B0000u, 46u}, // lab -> Lina
- {0x8C0B0000u, 30u}, // lad -> Hebr
- {0x980B0000u, 44u}, // lag -> Latn
+ {0x6B795452u, 46u}, // ky-TR -> Latn
+ {0x930A0000u, 46u}, // kye -> Latn
+ {0xDF0A0000u, 46u}, // kyx -> Latn
+ {0xA72A0000u, 46u}, // kzj -> Latn
+ {0xC72A0000u, 46u}, // kzr -> Latn
+ {0xCF2A0000u, 46u}, // kzt -> Latn
+ {0x6C610000u, 46u}, // la -> Latn
+ {0x840B0000u, 48u}, // lab -> Lina
+ {0x8C0B0000u, 31u}, // lad -> Hebr
+ {0x980B0000u, 46u}, // lag -> Latn
{0x9C0B0000u, 1u}, // lah -> Arab
- {0xA40B0000u, 44u}, // laj -> Latn
- {0xC80B0000u, 44u}, // las -> Latn
- {0x6C620000u, 44u}, // lb -> Latn
- {0x902B0000u, 16u}, // lbe -> Cyrl
- {0xD02B0000u, 44u}, // lbu -> Latn
- {0xD82B0000u, 44u}, // lbw -> Latn
- {0xB04B0000u, 44u}, // lcm -> Latn
- {0xBC4B0000u, 87u}, // lcp -> Thai
- {0x846B0000u, 44u}, // ldb -> Latn
- {0x8C8B0000u, 44u}, // led -> Latn
- {0x908B0000u, 44u}, // lee -> Latn
- {0xB08B0000u, 44u}, // lem -> Latn
- {0xBC8B0000u, 45u}, // lep -> Lepc
- {0xC08B0000u, 44u}, // leq -> Latn
- {0xD08B0000u, 44u}, // leu -> Latn
- {0xE48B0000u, 16u}, // lez -> Cyrl
- {0x6C670000u, 44u}, // lg -> Latn
- {0x98CB0000u, 44u}, // lgg -> Latn
- {0x6C690000u, 44u}, // li -> Latn
- {0x810B0000u, 44u}, // lia -> Latn
- {0x8D0B0000u, 44u}, // lid -> Latn
- {0x950B0000u, 17u}, // lif -> Deva
- {0x990B0000u, 44u}, // lig -> Latn
- {0x9D0B0000u, 44u}, // lih -> Latn
- {0xA50B0000u, 44u}, // lij -> Latn
- {0xC90B0000u, 47u}, // lis -> Lisu
- {0xBD2B0000u, 44u}, // ljp -> Latn
+ {0xA40B0000u, 46u}, // laj -> Latn
+ {0xC80B0000u, 46u}, // las -> Latn
+ {0x6C620000u, 46u}, // lb -> Latn
+ {0x902B0000u, 17u}, // lbe -> Cyrl
+ {0xD02B0000u, 46u}, // lbu -> Latn
+ {0xD82B0000u, 46u}, // lbw -> Latn
+ {0xB04B0000u, 46u}, // lcm -> Latn
+ {0xBC4B0000u, 89u}, // lcp -> Thai
+ {0x846B0000u, 46u}, // ldb -> Latn
+ {0x8C8B0000u, 46u}, // led -> Latn
+ {0x908B0000u, 46u}, // lee -> Latn
+ {0xB08B0000u, 46u}, // lem -> Latn
+ {0xBC8B0000u, 47u}, // lep -> Lepc
+ {0xC08B0000u, 46u}, // leq -> Latn
+ {0xD08B0000u, 46u}, // leu -> Latn
+ {0xE48B0000u, 17u}, // lez -> Cyrl
+ {0x6C670000u, 46u}, // lg -> Latn
+ {0x98CB0000u, 46u}, // lgg -> Latn
+ {0x6C690000u, 46u}, // li -> Latn
+ {0x810B0000u, 46u}, // lia -> Latn
+ {0x8D0B0000u, 46u}, // lid -> Latn
+ {0x950B0000u, 18u}, // lif -> Deva
+ {0x990B0000u, 46u}, // lig -> Latn
+ {0x9D0B0000u, 46u}, // lih -> Latn
+ {0xA50B0000u, 46u}, // lij -> Latn
+ {0xC90B0000u, 49u}, // lis -> Lisu
+ {0xBD2B0000u, 46u}, // ljp -> Latn
{0xA14B0000u, 1u}, // lki -> Arab
- {0xCD4B0000u, 44u}, // lkt -> Latn
- {0x916B0000u, 44u}, // lle -> Latn
- {0xB56B0000u, 44u}, // lln -> Latn
- {0xB58B0000u, 84u}, // lmn -> Telu
- {0xB98B0000u, 44u}, // lmo -> Latn
- {0xBD8B0000u, 44u}, // lmp -> Latn
- {0x6C6E0000u, 44u}, // ln -> Latn
- {0xC9AB0000u, 44u}, // lns -> Latn
- {0xD1AB0000u, 44u}, // lnu -> Latn
- {0x6C6F0000u, 43u}, // lo -> Laoo
- {0xA5CB0000u, 44u}, // loj -> Latn
- {0xA9CB0000u, 44u}, // lok -> Latn
- {0xADCB0000u, 44u}, // lol -> Latn
- {0xC5CB0000u, 44u}, // lor -> Latn
- {0xC9CB0000u, 44u}, // los -> Latn
- {0xE5CB0000u, 44u}, // loz -> Latn
+ {0xCD4B0000u, 46u}, // lkt -> Latn
+ {0x916B0000u, 46u}, // lle -> Latn
+ {0xB56B0000u, 46u}, // lln -> Latn
+ {0xB58B0000u, 86u}, // lmn -> Telu
+ {0xB98B0000u, 46u}, // lmo -> Latn
+ {0xBD8B0000u, 46u}, // lmp -> Latn
+ {0x6C6E0000u, 46u}, // ln -> Latn
+ {0xC9AB0000u, 46u}, // lns -> Latn
+ {0xD1AB0000u, 46u}, // lnu -> Latn
+ {0x6C6F0000u, 45u}, // lo -> Laoo
+ {0xA5CB0000u, 46u}, // loj -> Latn
+ {0xA9CB0000u, 46u}, // lok -> Latn
+ {0xADCB0000u, 46u}, // lol -> Latn
+ {0xC5CB0000u, 46u}, // lor -> Latn
+ {0xC9CB0000u, 46u}, // los -> Latn
+ {0xE5CB0000u, 46u}, // loz -> Latn
{0x8A2B0000u, 1u}, // lrc -> Arab
- {0x6C740000u, 44u}, // lt -> Latn
- {0x9A6B0000u, 44u}, // ltg -> Latn
- {0x6C750000u, 44u}, // lu -> Latn
- {0x828B0000u, 44u}, // lua -> Latn
- {0xBA8B0000u, 44u}, // luo -> Latn
- {0xE28B0000u, 44u}, // luy -> Latn
+ {0x6C740000u, 46u}, // lt -> Latn
+ {0x9A6B0000u, 46u}, // ltg -> Latn
+ {0x6C750000u, 46u}, // lu -> Latn
+ {0x828B0000u, 46u}, // lua -> Latn
+ {0xBA8B0000u, 46u}, // luo -> Latn
+ {0xE28B0000u, 46u}, // luy -> Latn
{0xE68B0000u, 1u}, // luz -> Arab
- {0x6C760000u, 44u}, // lv -> Latn
- {0xAECB0000u, 87u}, // lwl -> Thai
- {0x9F2B0000u, 27u}, // lzh -> Hans
- {0xE72B0000u, 44u}, // lzz -> Latn
- {0x8C0C0000u, 44u}, // mad -> Latn
- {0x940C0000u, 44u}, // maf -> Latn
- {0x980C0000u, 17u}, // mag -> Deva
- {0xA00C0000u, 17u}, // mai -> Deva
- {0xA80C0000u, 44u}, // mak -> Latn
- {0xB40C0000u, 44u}, // man -> Latn
- {0xB40C474Eu, 58u}, // man-GN -> Nkoo
- {0xC80C0000u, 44u}, // mas -> Latn
- {0xD80C0000u, 44u}, // maw -> Latn
- {0xE40C0000u, 44u}, // maz -> Latn
- {0x9C2C0000u, 44u}, // mbh -> Latn
- {0xB82C0000u, 44u}, // mbo -> Latn
- {0xC02C0000u, 44u}, // mbq -> Latn
- {0xD02C0000u, 44u}, // mbu -> Latn
- {0xD82C0000u, 44u}, // mbw -> Latn
- {0xA04C0000u, 44u}, // mci -> Latn
- {0xBC4C0000u, 44u}, // mcp -> Latn
- {0xC04C0000u, 44u}, // mcq -> Latn
- {0xC44C0000u, 44u}, // mcr -> Latn
- {0xD04C0000u, 44u}, // mcu -> Latn
- {0x806C0000u, 44u}, // mda -> Latn
+ {0x6C760000u, 46u}, // lv -> Latn
+ {0xAECB0000u, 89u}, // lwl -> Thai
+ {0x9F2B0000u, 28u}, // lzh -> Hans
+ {0xE72B0000u, 46u}, // lzz -> Latn
+ {0x8C0C0000u, 46u}, // mad -> Latn
+ {0x940C0000u, 46u}, // maf -> Latn
+ {0x980C0000u, 18u}, // mag -> Deva
+ {0xA00C0000u, 18u}, // mai -> Deva
+ {0xA80C0000u, 46u}, // mak -> Latn
+ {0xB40C0000u, 46u}, // man -> Latn
+ {0xB40C474Eu, 60u}, // man-GN -> Nkoo
+ {0xC80C0000u, 46u}, // mas -> Latn
+ {0xD80C0000u, 46u}, // maw -> Latn
+ {0xE40C0000u, 46u}, // maz -> Latn
+ {0x9C2C0000u, 46u}, // mbh -> Latn
+ {0xB82C0000u, 46u}, // mbo -> Latn
+ {0xC02C0000u, 46u}, // mbq -> Latn
+ {0xD02C0000u, 46u}, // mbu -> Latn
+ {0xD82C0000u, 46u}, // mbw -> Latn
+ {0xA04C0000u, 46u}, // mci -> Latn
+ {0xBC4C0000u, 46u}, // mcp -> Latn
+ {0xC04C0000u, 46u}, // mcq -> Latn
+ {0xC44C0000u, 46u}, // mcr -> Latn
+ {0xD04C0000u, 46u}, // mcu -> Latn
+ {0x806C0000u, 46u}, // mda -> Latn
{0x906C0000u, 1u}, // mde -> Arab
- {0x946C0000u, 16u}, // mdf -> Cyrl
- {0x9C6C0000u, 44u}, // mdh -> Latn
- {0xA46C0000u, 44u}, // mdj -> Latn
- {0xC46C0000u, 44u}, // mdr -> Latn
- {0xDC6C0000u, 19u}, // mdx -> Ethi
- {0x8C8C0000u, 44u}, // med -> Latn
- {0x908C0000u, 44u}, // mee -> Latn
- {0xA88C0000u, 44u}, // mek -> Latn
- {0xB48C0000u, 44u}, // men -> Latn
- {0xC48C0000u, 44u}, // mer -> Latn
- {0xCC8C0000u, 44u}, // met -> Latn
- {0xD08C0000u, 44u}, // meu -> Latn
+ {0x946C0000u, 17u}, // mdf -> Cyrl
+ {0x9C6C0000u, 46u}, // mdh -> Latn
+ {0xA46C0000u, 46u}, // mdj -> Latn
+ {0xC46C0000u, 46u}, // mdr -> Latn
+ {0xDC6C0000u, 20u}, // mdx -> Ethi
+ {0x8C8C0000u, 46u}, // med -> Latn
+ {0x908C0000u, 46u}, // mee -> Latn
+ {0xA88C0000u, 46u}, // mek -> Latn
+ {0xB48C0000u, 46u}, // men -> Latn
+ {0xC48C0000u, 46u}, // mer -> Latn
+ {0xCC8C0000u, 46u}, // met -> Latn
+ {0xD08C0000u, 46u}, // meu -> Latn
{0x80AC0000u, 1u}, // mfa -> Arab
- {0x90AC0000u, 44u}, // mfe -> Latn
- {0xB4AC0000u, 44u}, // mfn -> Latn
- {0xB8AC0000u, 44u}, // mfo -> Latn
- {0xC0AC0000u, 44u}, // mfq -> Latn
- {0x6D670000u, 44u}, // mg -> Latn
- {0x9CCC0000u, 44u}, // mgh -> Latn
- {0xACCC0000u, 44u}, // mgl -> Latn
- {0xB8CC0000u, 44u}, // mgo -> Latn
- {0xBCCC0000u, 17u}, // mgp -> Deva
- {0xE0CC0000u, 44u}, // mgy -> Latn
- {0x6D680000u, 44u}, // mh -> Latn
- {0xA0EC0000u, 44u}, // mhi -> Latn
- {0xACEC0000u, 44u}, // mhl -> Latn
- {0x6D690000u, 44u}, // mi -> Latn
- {0x950C0000u, 44u}, // mif -> Latn
- {0xB50C0000u, 44u}, // min -> Latn
- {0xC90C0000u, 29u}, // mis -> Hatr
- {0xD90C0000u, 44u}, // miw -> Latn
- {0x6D6B0000u, 16u}, // mk -> Cyrl
+ {0x90AC0000u, 46u}, // mfe -> Latn
+ {0xB4AC0000u, 46u}, // mfn -> Latn
+ {0xB8AC0000u, 46u}, // mfo -> Latn
+ {0xC0AC0000u, 46u}, // mfq -> Latn
+ {0x6D670000u, 46u}, // mg -> Latn
+ {0x9CCC0000u, 46u}, // mgh -> Latn
+ {0xACCC0000u, 46u}, // mgl -> Latn
+ {0xB8CC0000u, 46u}, // mgo -> Latn
+ {0xBCCC0000u, 18u}, // mgp -> Deva
+ {0xE0CC0000u, 46u}, // mgy -> Latn
+ {0x6D680000u, 46u}, // mh -> Latn
+ {0xA0EC0000u, 46u}, // mhi -> Latn
+ {0xACEC0000u, 46u}, // mhl -> Latn
+ {0x6D690000u, 46u}, // mi -> Latn
+ {0x950C0000u, 46u}, // mif -> Latn
+ {0xB50C0000u, 46u}, // min -> Latn
+ {0xC90C0000u, 30u}, // mis -> Hatr
+ {0xD90C0000u, 46u}, // miw -> Latn
+ {0x6D6B0000u, 17u}, // mk -> Cyrl
{0xA14C0000u, 1u}, // mki -> Arab
- {0xAD4C0000u, 44u}, // mkl -> Latn
- {0xBD4C0000u, 44u}, // mkp -> Latn
- {0xD94C0000u, 44u}, // mkw -> Latn
- {0x6D6C0000u, 53u}, // ml -> Mlym
- {0x916C0000u, 44u}, // mle -> Latn
- {0xBD6C0000u, 44u}, // mlp -> Latn
- {0xC96C0000u, 44u}, // mls -> Latn
- {0xB98C0000u, 44u}, // mmo -> Latn
- {0xD18C0000u, 44u}, // mmu -> Latn
- {0xDD8C0000u, 44u}, // mmx -> Latn
- {0x6D6E0000u, 16u}, // mn -> Cyrl
- {0x6D6E434Eu, 54u}, // mn-CN -> Mong
- {0x81AC0000u, 44u}, // mna -> Latn
- {0x95AC0000u, 44u}, // mnf -> Latn
+ {0xAD4C0000u, 46u}, // mkl -> Latn
+ {0xBD4C0000u, 46u}, // mkp -> Latn
+ {0xD94C0000u, 46u}, // mkw -> Latn
+ {0x6D6C0000u, 55u}, // ml -> Mlym
+ {0x916C0000u, 46u}, // mle -> Latn
+ {0xBD6C0000u, 46u}, // mlp -> Latn
+ {0xC96C0000u, 46u}, // mls -> Latn
+ {0xB98C0000u, 46u}, // mmo -> Latn
+ {0xD18C0000u, 46u}, // mmu -> Latn
+ {0xDD8C0000u, 46u}, // mmx -> Latn
+ {0x6D6E0000u, 17u}, // mn -> Cyrl
+ {0x6D6E434Eu, 56u}, // mn-CN -> Mong
+ {0x81AC0000u, 46u}, // mna -> Latn
+ {0x95AC0000u, 46u}, // mnf -> Latn
{0xA1AC0000u, 7u}, // mni -> Beng
- {0xD9AC0000u, 56u}, // mnw -> Mymr
- {0x6D6F0000u, 44u}, // mo -> Latn
- {0x81CC0000u, 44u}, // moa -> Latn
- {0x91CC0000u, 44u}, // moe -> Latn
- {0x9DCC0000u, 44u}, // moh -> Latn
- {0xC9CC0000u, 44u}, // mos -> Latn
- {0xDDCC0000u, 44u}, // mox -> Latn
- {0xBDEC0000u, 44u}, // mpp -> Latn
- {0xC9EC0000u, 44u}, // mps -> Latn
- {0xCDEC0000u, 44u}, // mpt -> Latn
- {0xDDEC0000u, 44u}, // mpx -> Latn
- {0xAE0C0000u, 44u}, // mql -> Latn
- {0x6D720000u, 17u}, // mr -> Deva
- {0x8E2C0000u, 17u}, // mrd -> Deva
- {0xA62C0000u, 16u}, // mrj -> Cyrl
- {0xBA2C0000u, 55u}, // mro -> Mroo
- {0x6D730000u, 44u}, // ms -> Latn
+ {0xD9AC0000u, 58u}, // mnw -> Mymr
+ {0x6D6F0000u, 46u}, // mo -> Latn
+ {0x81CC0000u, 46u}, // moa -> Latn
+ {0x91CC0000u, 46u}, // moe -> Latn
+ {0x9DCC0000u, 46u}, // moh -> Latn
+ {0xC9CC0000u, 46u}, // mos -> Latn
+ {0xDDCC0000u, 46u}, // mox -> Latn
+ {0xBDEC0000u, 46u}, // mpp -> Latn
+ {0xC9EC0000u, 46u}, // mps -> Latn
+ {0xCDEC0000u, 46u}, // mpt -> Latn
+ {0xDDEC0000u, 46u}, // mpx -> Latn
+ {0xAE0C0000u, 46u}, // mql -> Latn
+ {0x6D720000u, 18u}, // mr -> Deva
+ {0x8E2C0000u, 18u}, // mrd -> Deva
+ {0xA62C0000u, 17u}, // mrj -> Cyrl
+ {0xBA2C0000u, 57u}, // mro -> Mroo
+ {0x6D730000u, 46u}, // ms -> Latn
{0x6D734343u, 1u}, // ms-CC -> Arab
{0x6D734944u, 1u}, // ms-ID -> Arab
- {0x6D740000u, 44u}, // mt -> Latn
- {0x8A6C0000u, 44u}, // mtc -> Latn
- {0x966C0000u, 44u}, // mtf -> Latn
- {0xA26C0000u, 44u}, // mti -> Latn
- {0xC66C0000u, 17u}, // mtr -> Deva
- {0x828C0000u, 44u}, // mua -> Latn
- {0xC68C0000u, 44u}, // mur -> Latn
- {0xCA8C0000u, 44u}, // mus -> Latn
- {0x82AC0000u, 44u}, // mva -> Latn
- {0xB6AC0000u, 44u}, // mvn -> Latn
+ {0x6D740000u, 46u}, // mt -> Latn
+ {0x8A6C0000u, 46u}, // mtc -> Latn
+ {0x966C0000u, 46u}, // mtf -> Latn
+ {0xA26C0000u, 46u}, // mti -> Latn
+ {0xC66C0000u, 18u}, // mtr -> Deva
+ {0x828C0000u, 46u}, // mua -> Latn
+ {0xC68C0000u, 46u}, // mur -> Latn
+ {0xCA8C0000u, 46u}, // mus -> Latn
+ {0x82AC0000u, 46u}, // mva -> Latn
+ {0xB6AC0000u, 46u}, // mvn -> Latn
{0xE2AC0000u, 1u}, // mvy -> Arab
- {0xAACC0000u, 44u}, // mwk -> Latn
- {0xC6CC0000u, 17u}, // mwr -> Deva
- {0xD6CC0000u, 44u}, // mwv -> Latn
- {0xDACC0000u, 33u}, // mww -> Hmnp
- {0x8AEC0000u, 44u}, // mxc -> Latn
- {0xB2EC0000u, 44u}, // mxm -> Latn
- {0x6D790000u, 56u}, // my -> Mymr
- {0xAB0C0000u, 44u}, // myk -> Latn
- {0xB30C0000u, 19u}, // mym -> Ethi
- {0xD70C0000u, 16u}, // myv -> Cyrl
- {0xDB0C0000u, 44u}, // myw -> Latn
- {0xDF0C0000u, 44u}, // myx -> Latn
- {0xE70C0000u, 50u}, // myz -> Mand
- {0xAB2C0000u, 44u}, // mzk -> Latn
- {0xB32C0000u, 44u}, // mzm -> Latn
+ {0xAACC0000u, 46u}, // mwk -> Latn
+ {0xC6CC0000u, 18u}, // mwr -> Deva
+ {0xD6CC0000u, 46u}, // mwv -> Latn
+ {0xDACC0000u, 34u}, // mww -> Hmnp
+ {0x8AEC0000u, 46u}, // mxc -> Latn
+ {0xB2EC0000u, 46u}, // mxm -> Latn
+ {0x6D790000u, 58u}, // my -> Mymr
+ {0xAB0C0000u, 46u}, // myk -> Latn
+ {0xB30C0000u, 20u}, // mym -> Ethi
+ {0xD70C0000u, 17u}, // myv -> Cyrl
+ {0xDB0C0000u, 46u}, // myw -> Latn
+ {0xDF0C0000u, 46u}, // myx -> Latn
+ {0xE70C0000u, 52u}, // myz -> Mand
+ {0xAB2C0000u, 46u}, // mzk -> Latn
+ {0xB32C0000u, 46u}, // mzm -> Latn
{0xB72C0000u, 1u}, // mzn -> Arab
- {0xBF2C0000u, 44u}, // mzp -> Latn
- {0xDB2C0000u, 44u}, // mzw -> Latn
- {0xE72C0000u, 44u}, // mzz -> Latn
- {0x6E610000u, 44u}, // na -> Latn
- {0x880D0000u, 44u}, // nac -> Latn
- {0x940D0000u, 44u}, // naf -> Latn
- {0xA80D0000u, 44u}, // nak -> Latn
- {0xB40D0000u, 27u}, // nan -> Hans
- {0xBC0D0000u, 44u}, // nap -> Latn
- {0xC00D0000u, 44u}, // naq -> Latn
- {0xC80D0000u, 44u}, // nas -> Latn
- {0x6E620000u, 44u}, // nb -> Latn
- {0x804D0000u, 44u}, // nca -> Latn
- {0x904D0000u, 44u}, // nce -> Latn
- {0x944D0000u, 44u}, // ncf -> Latn
- {0x9C4D0000u, 44u}, // nch -> Latn
- {0xB84D0000u, 44u}, // nco -> Latn
- {0xD04D0000u, 44u}, // ncu -> Latn
- {0x6E640000u, 44u}, // nd -> Latn
- {0x886D0000u, 44u}, // ndc -> Latn
- {0xC86D0000u, 44u}, // nds -> Latn
- {0x6E650000u, 17u}, // ne -> Deva
- {0x848D0000u, 44u}, // neb -> Latn
- {0xD88D0000u, 17u}, // new -> Deva
- {0xDC8D0000u, 44u}, // nex -> Latn
- {0xC4AD0000u, 44u}, // nfr -> Latn
- {0x6E670000u, 44u}, // ng -> Latn
- {0x80CD0000u, 44u}, // nga -> Latn
- {0x84CD0000u, 44u}, // ngb -> Latn
- {0xACCD0000u, 44u}, // ngl -> Latn
- {0x84ED0000u, 44u}, // nhb -> Latn
- {0x90ED0000u, 44u}, // nhe -> Latn
- {0xD8ED0000u, 44u}, // nhw -> Latn
- {0x950D0000u, 44u}, // nif -> Latn
- {0xA10D0000u, 44u}, // nii -> Latn
- {0xA50D0000u, 44u}, // nij -> Latn
- {0xB50D0000u, 44u}, // nin -> Latn
- {0xD10D0000u, 44u}, // niu -> Latn
- {0xE10D0000u, 44u}, // niy -> Latn
- {0xE50D0000u, 44u}, // niz -> Latn
- {0xB92D0000u, 44u}, // njo -> Latn
- {0x994D0000u, 44u}, // nkg -> Latn
- {0xB94D0000u, 44u}, // nko -> Latn
- {0x6E6C0000u, 44u}, // nl -> Latn
- {0x998D0000u, 44u}, // nmg -> Latn
- {0xE58D0000u, 44u}, // nmz -> Latn
- {0x6E6E0000u, 44u}, // nn -> Latn
- {0x95AD0000u, 44u}, // nnf -> Latn
- {0x9DAD0000u, 44u}, // nnh -> Latn
- {0xA9AD0000u, 44u}, // nnk -> Latn
- {0xB1AD0000u, 44u}, // nnm -> Latn
- {0xBDAD0000u, 91u}, // nnp -> Wcho
- {0x6E6F0000u, 44u}, // no -> Latn
- {0x8DCD0000u, 42u}, // nod -> Lana
- {0x91CD0000u, 17u}, // noe -> Deva
- {0xB5CD0000u, 69u}, // non -> Runr
- {0xBDCD0000u, 44u}, // nop -> Latn
- {0xD1CD0000u, 44u}, // nou -> Latn
- {0xBA0D0000u, 58u}, // nqo -> Nkoo
- {0x6E720000u, 44u}, // nr -> Latn
- {0x862D0000u, 44u}, // nrb -> Latn
+ {0xBF2C0000u, 46u}, // mzp -> Latn
+ {0xDB2C0000u, 46u}, // mzw -> Latn
+ {0xE72C0000u, 46u}, // mzz -> Latn
+ {0x6E610000u, 46u}, // na -> Latn
+ {0x880D0000u, 46u}, // nac -> Latn
+ {0x940D0000u, 46u}, // naf -> Latn
+ {0xA80D0000u, 46u}, // nak -> Latn
+ {0xB40D0000u, 28u}, // nan -> Hans
+ {0xBC0D0000u, 46u}, // nap -> Latn
+ {0xC00D0000u, 46u}, // naq -> Latn
+ {0xC80D0000u, 46u}, // nas -> Latn
+ {0x6E620000u, 46u}, // nb -> Latn
+ {0x804D0000u, 46u}, // nca -> Latn
+ {0x904D0000u, 46u}, // nce -> Latn
+ {0x944D0000u, 46u}, // ncf -> Latn
+ {0x9C4D0000u, 46u}, // nch -> Latn
+ {0xB84D0000u, 46u}, // nco -> Latn
+ {0xD04D0000u, 46u}, // ncu -> Latn
+ {0x6E640000u, 46u}, // nd -> Latn
+ {0x886D0000u, 46u}, // ndc -> Latn
+ {0xC86D0000u, 46u}, // nds -> Latn
+ {0x6E650000u, 18u}, // ne -> Deva
+ {0x848D0000u, 46u}, // neb -> Latn
+ {0xD88D0000u, 18u}, // new -> Deva
+ {0xDC8D0000u, 46u}, // nex -> Latn
+ {0xC4AD0000u, 46u}, // nfr -> Latn
+ {0x6E670000u, 46u}, // ng -> Latn
+ {0x80CD0000u, 46u}, // nga -> Latn
+ {0x84CD0000u, 46u}, // ngb -> Latn
+ {0xACCD0000u, 46u}, // ngl -> Latn
+ {0x84ED0000u, 46u}, // nhb -> Latn
+ {0x90ED0000u, 46u}, // nhe -> Latn
+ {0xD8ED0000u, 46u}, // nhw -> Latn
+ {0x950D0000u, 46u}, // nif -> Latn
+ {0xA10D0000u, 46u}, // nii -> Latn
+ {0xA50D0000u, 46u}, // nij -> Latn
+ {0xB50D0000u, 46u}, // nin -> Latn
+ {0xD10D0000u, 46u}, // niu -> Latn
+ {0xE10D0000u, 46u}, // niy -> Latn
+ {0xE50D0000u, 46u}, // niz -> Latn
+ {0xB92D0000u, 46u}, // njo -> Latn
+ {0x994D0000u, 46u}, // nkg -> Latn
+ {0xB94D0000u, 46u}, // nko -> Latn
+ {0x6E6C0000u, 46u}, // nl -> Latn
+ {0x998D0000u, 46u}, // nmg -> Latn
+ {0xE58D0000u, 46u}, // nmz -> Latn
+ {0x6E6E0000u, 46u}, // nn -> Latn
+ {0x95AD0000u, 46u}, // nnf -> Latn
+ {0x9DAD0000u, 46u}, // nnh -> Latn
+ {0xA9AD0000u, 46u}, // nnk -> Latn
+ {0xB1AD0000u, 46u}, // nnm -> Latn
+ {0xBDAD0000u, 93u}, // nnp -> Wcho
+ {0x6E6F0000u, 46u}, // no -> Latn
+ {0x8DCD0000u, 44u}, // nod -> Lana
+ {0x91CD0000u, 18u}, // noe -> Deva
+ {0xB5CD0000u, 71u}, // non -> Runr
+ {0xBDCD0000u, 46u}, // nop -> Latn
+ {0xD1CD0000u, 46u}, // nou -> Latn
+ {0xBA0D0000u, 60u}, // nqo -> Nkoo
+ {0x6E720000u, 46u}, // nr -> Latn
+ {0x862D0000u, 46u}, // nrb -> Latn
{0xAA4D0000u, 10u}, // nsk -> Cans
- {0xB64D0000u, 44u}, // nsn -> Latn
- {0xBA4D0000u, 44u}, // nso -> Latn
- {0xCA4D0000u, 44u}, // nss -> Latn
- {0xB26D0000u, 44u}, // ntm -> Latn
- {0xC66D0000u, 44u}, // ntr -> Latn
- {0xA28D0000u, 44u}, // nui -> Latn
- {0xBE8D0000u, 44u}, // nup -> Latn
- {0xCA8D0000u, 44u}, // nus -> Latn
- {0xD68D0000u, 44u}, // nuv -> Latn
- {0xDE8D0000u, 44u}, // nux -> Latn
- {0x6E760000u, 44u}, // nv -> Latn
- {0x86CD0000u, 44u}, // nwb -> Latn
- {0xC2ED0000u, 44u}, // nxq -> Latn
- {0xC6ED0000u, 44u}, // nxr -> Latn
- {0x6E790000u, 44u}, // ny -> Latn
- {0xB30D0000u, 44u}, // nym -> Latn
- {0xB70D0000u, 44u}, // nyn -> Latn
- {0xA32D0000u, 44u}, // nzi -> Latn
- {0x6F630000u, 44u}, // oc -> Latn
- {0x88CE0000u, 44u}, // ogc -> Latn
- {0xC54E0000u, 44u}, // okr -> Latn
- {0xD54E0000u, 44u}, // okv -> Latn
- {0x6F6D0000u, 44u}, // om -> Latn
- {0x99AE0000u, 44u}, // ong -> Latn
- {0xB5AE0000u, 44u}, // onn -> Latn
- {0xC9AE0000u, 44u}, // ons -> Latn
- {0xB1EE0000u, 44u}, // opm -> Latn
- {0x6F720000u, 62u}, // or -> Orya
- {0xBA2E0000u, 44u}, // oro -> Latn
+ {0xB64D0000u, 46u}, // nsn -> Latn
+ {0xBA4D0000u, 46u}, // nso -> Latn
+ {0xCA4D0000u, 46u}, // nss -> Latn
+ {0xB26D0000u, 46u}, // ntm -> Latn
+ {0xC66D0000u, 46u}, // ntr -> Latn
+ {0xA28D0000u, 46u}, // nui -> Latn
+ {0xBE8D0000u, 46u}, // nup -> Latn
+ {0xCA8D0000u, 46u}, // nus -> Latn
+ {0xD68D0000u, 46u}, // nuv -> Latn
+ {0xDE8D0000u, 46u}, // nux -> Latn
+ {0x6E760000u, 46u}, // nv -> Latn
+ {0x86CD0000u, 46u}, // nwb -> Latn
+ {0xC2ED0000u, 46u}, // nxq -> Latn
+ {0xC6ED0000u, 46u}, // nxr -> Latn
+ {0x6E790000u, 46u}, // ny -> Latn
+ {0xB30D0000u, 46u}, // nym -> Latn
+ {0xB70D0000u, 46u}, // nyn -> Latn
+ {0xA32D0000u, 46u}, // nzi -> Latn
+ {0x6F630000u, 46u}, // oc -> Latn
+ {0x88CE0000u, 46u}, // ogc -> Latn
+ {0xC54E0000u, 46u}, // okr -> Latn
+ {0xD54E0000u, 46u}, // okv -> Latn
+ {0x6F6D0000u, 46u}, // om -> Latn
+ {0x99AE0000u, 46u}, // ong -> Latn
+ {0xB5AE0000u, 46u}, // onn -> Latn
+ {0xC9AE0000u, 46u}, // ons -> Latn
+ {0xB1EE0000u, 46u}, // opm -> Latn
+ {0x6F720000u, 64u}, // or -> Orya
+ {0xBA2E0000u, 46u}, // oro -> Latn
{0xD22E0000u, 1u}, // oru -> Arab
- {0x6F730000u, 16u}, // os -> Cyrl
- {0x824E0000u, 63u}, // osa -> Osge
+ {0x6F730000u, 17u}, // os -> Cyrl
+ {0x824E0000u, 65u}, // osa -> Osge
{0x826E0000u, 1u}, // ota -> Arab
- {0xAA6E0000u, 61u}, // otk -> Orkh
- {0xB32E0000u, 44u}, // ozm -> Latn
- {0x70610000u, 26u}, // pa -> Guru
+ {0xAA6E0000u, 63u}, // otk -> Orkh
+ {0xB32E0000u, 46u}, // ozm -> Latn
+ {0x70610000u, 27u}, // pa -> Guru
{0x7061504Bu, 1u}, // pa-PK -> Arab
- {0x980F0000u, 44u}, // pag -> Latn
- {0xAC0F0000u, 65u}, // pal -> Phli
- {0xB00F0000u, 44u}, // pam -> Latn
- {0xBC0F0000u, 44u}, // pap -> Latn
- {0xD00F0000u, 44u}, // pau -> Latn
- {0xA02F0000u, 44u}, // pbi -> Latn
- {0x8C4F0000u, 44u}, // pcd -> Latn
- {0xB04F0000u, 44u}, // pcm -> Latn
- {0x886F0000u, 44u}, // pdc -> Latn
- {0xCC6F0000u, 44u}, // pdt -> Latn
- {0x8C8F0000u, 44u}, // ped -> Latn
- {0xB88F0000u, 92u}, // peo -> Xpeo
- {0xDC8F0000u, 44u}, // pex -> Latn
- {0xACAF0000u, 44u}, // pfl -> Latn
+ {0x980F0000u, 46u}, // pag -> Latn
+ {0xAC0F0000u, 67u}, // pal -> Phli
+ {0xB00F0000u, 46u}, // pam -> Latn
+ {0xBC0F0000u, 46u}, // pap -> Latn
+ {0xD00F0000u, 46u}, // pau -> Latn
+ {0xA02F0000u, 46u}, // pbi -> Latn
+ {0x8C4F0000u, 46u}, // pcd -> Latn
+ {0xB04F0000u, 46u}, // pcm -> Latn
+ {0x886F0000u, 46u}, // pdc -> Latn
+ {0xCC6F0000u, 46u}, // pdt -> Latn
+ {0x8C8F0000u, 46u}, // ped -> Latn
+ {0xB88F0000u, 94u}, // peo -> Xpeo
+ {0xDC8F0000u, 46u}, // pex -> Latn
+ {0xACAF0000u, 46u}, // pfl -> Latn
{0xACEF0000u, 1u}, // phl -> Arab
- {0xB4EF0000u, 66u}, // phn -> Phnx
- {0xAD0F0000u, 44u}, // pil -> Latn
- {0xBD0F0000u, 44u}, // pip -> Latn
+ {0xB4EF0000u, 68u}, // phn -> Phnx
+ {0xAD0F0000u, 46u}, // pil -> Latn
+ {0xBD0F0000u, 46u}, // pip -> Latn
{0x814F0000u, 8u}, // pka -> Brah
- {0xB94F0000u, 44u}, // pko -> Latn
- {0x706C0000u, 44u}, // pl -> Latn
- {0x816F0000u, 44u}, // pla -> Latn
- {0xC98F0000u, 44u}, // pms -> Latn
- {0x99AF0000u, 44u}, // png -> Latn
- {0xB5AF0000u, 44u}, // pnn -> Latn
- {0xCDAF0000u, 24u}, // pnt -> Grek
- {0xB5CF0000u, 44u}, // pon -> Latn
- {0x81EF0000u, 17u}, // ppa -> Deva
- {0xB9EF0000u, 44u}, // ppo -> Latn
- {0x822F0000u, 38u}, // pra -> Khar
+ {0xB94F0000u, 46u}, // pko -> Latn
+ {0x706C0000u, 46u}, // pl -> Latn
+ {0x816F0000u, 46u}, // pla -> Latn
+ {0xC98F0000u, 46u}, // pms -> Latn
+ {0x99AF0000u, 46u}, // png -> Latn
+ {0xB5AF0000u, 46u}, // pnn -> Latn
+ {0xCDAF0000u, 25u}, // pnt -> Grek
+ {0xB5CF0000u, 46u}, // pon -> Latn
+ {0x81EF0000u, 18u}, // ppa -> Deva
+ {0xB9EF0000u, 46u}, // ppo -> Latn
+ {0x822F0000u, 39u}, // pra -> Khar
{0x8E2F0000u, 1u}, // prd -> Arab
- {0x9A2F0000u, 44u}, // prg -> Latn
+ {0x9A2F0000u, 46u}, // prg -> Latn
{0x70730000u, 1u}, // ps -> Arab
- {0xCA4F0000u, 44u}, // pss -> Latn
- {0x70740000u, 44u}, // pt -> Latn
- {0xBE6F0000u, 44u}, // ptp -> Latn
- {0xD28F0000u, 44u}, // puu -> Latn
- {0x82CF0000u, 44u}, // pwa -> Latn
- {0x71750000u, 44u}, // qu -> Latn
- {0x8A900000u, 44u}, // quc -> Latn
- {0x9A900000u, 44u}, // qug -> Latn
- {0xA0110000u, 44u}, // rai -> Latn
- {0xA4110000u, 17u}, // raj -> Deva
- {0xB8110000u, 44u}, // rao -> Latn
- {0x94510000u, 44u}, // rcf -> Latn
- {0xA4910000u, 44u}, // rej -> Latn
- {0xAC910000u, 44u}, // rel -> Latn
- {0xC8910000u, 44u}, // res -> Latn
- {0xB4D10000u, 44u}, // rgn -> Latn
+ {0xCA4F0000u, 46u}, // pss -> Latn
+ {0x70740000u, 46u}, // pt -> Latn
+ {0xBE6F0000u, 46u}, // ptp -> Latn
+ {0xD28F0000u, 46u}, // puu -> Latn
+ {0x82CF0000u, 46u}, // pwa -> Latn
+ {0x71750000u, 46u}, // qu -> Latn
+ {0x8A900000u, 46u}, // quc -> Latn
+ {0x9A900000u, 46u}, // qug -> Latn
+ {0xA0110000u, 46u}, // rai -> Latn
+ {0xA4110000u, 18u}, // raj -> Deva
+ {0xB8110000u, 46u}, // rao -> Latn
+ {0x94510000u, 46u}, // rcf -> Latn
+ {0xA4910000u, 46u}, // rej -> Latn
+ {0xAC910000u, 46u}, // rel -> Latn
+ {0xC8910000u, 46u}, // res -> Latn
+ {0xB4D10000u, 46u}, // rgn -> Latn
{0x98F10000u, 1u}, // rhg -> Arab
- {0x81110000u, 44u}, // ria -> Latn
- {0x95110000u, 85u}, // rif -> Tfng
- {0x95114E4Cu, 44u}, // rif-NL -> Latn
- {0xC9310000u, 17u}, // rjs -> Deva
+ {0x81110000u, 46u}, // ria -> Latn
+ {0x95110000u, 87u}, // rif -> Tfng
+ {0x95114E4Cu, 46u}, // rif-NL -> Latn
+ {0xC9310000u, 18u}, // rjs -> Deva
{0xCD510000u, 7u}, // rkt -> Beng
- {0x726D0000u, 44u}, // rm -> Latn
- {0x95910000u, 44u}, // rmf -> Latn
- {0xB9910000u, 44u}, // rmo -> Latn
+ {0x726D0000u, 46u}, // rm -> Latn
+ {0x95910000u, 46u}, // rmf -> Latn
+ {0xB9910000u, 46u}, // rmo -> Latn
{0xCD910000u, 1u}, // rmt -> Arab
- {0xD1910000u, 44u}, // rmu -> Latn
- {0x726E0000u, 44u}, // rn -> Latn
- {0x81B10000u, 44u}, // rna -> Latn
- {0x99B10000u, 44u}, // rng -> Latn
- {0x726F0000u, 44u}, // ro -> Latn
- {0x85D10000u, 44u}, // rob -> Latn
- {0x95D10000u, 44u}, // rof -> Latn
- {0xB9D10000u, 44u}, // roo -> Latn
- {0xBA310000u, 44u}, // rro -> Latn
- {0xB2710000u, 44u}, // rtm -> Latn
- {0x72750000u, 16u}, // ru -> Cyrl
- {0x92910000u, 16u}, // rue -> Cyrl
- {0x9A910000u, 44u}, // rug -> Latn
- {0x72770000u, 44u}, // rw -> Latn
- {0xAAD10000u, 44u}, // rwk -> Latn
- {0xBAD10000u, 44u}, // rwo -> Latn
- {0xD3110000u, 37u}, // ryu -> Kana
- {0x73610000u, 17u}, // sa -> Deva
- {0x94120000u, 44u}, // saf -> Latn
- {0x9C120000u, 16u}, // sah -> Cyrl
- {0xC0120000u, 44u}, // saq -> Latn
- {0xC8120000u, 44u}, // sas -> Latn
- {0xCC120000u, 44u}, // sat -> Latn
- {0xD4120000u, 44u}, // sav -> Latn
- {0xE4120000u, 72u}, // saz -> Saur
- {0x80320000u, 44u}, // sba -> Latn
- {0x90320000u, 44u}, // sbe -> Latn
- {0xBC320000u, 44u}, // sbp -> Latn
- {0x73630000u, 44u}, // sc -> Latn
- {0xA8520000u, 17u}, // sck -> Deva
+ {0xD1910000u, 46u}, // rmu -> Latn
+ {0x726E0000u, 46u}, // rn -> Latn
+ {0x81B10000u, 46u}, // rna -> Latn
+ {0x99B10000u, 46u}, // rng -> Latn
+ {0x726F0000u, 46u}, // ro -> Latn
+ {0x85D10000u, 46u}, // rob -> Latn
+ {0x95D10000u, 46u}, // rof -> Latn
+ {0xB9D10000u, 46u}, // roo -> Latn
+ {0xBA310000u, 46u}, // rro -> Latn
+ {0xB2710000u, 46u}, // rtm -> Latn
+ {0x72750000u, 17u}, // ru -> Cyrl
+ {0x92910000u, 17u}, // rue -> Cyrl
+ {0x9A910000u, 46u}, // rug -> Latn
+ {0x72770000u, 46u}, // rw -> Latn
+ {0xAAD10000u, 46u}, // rwk -> Latn
+ {0xBAD10000u, 46u}, // rwo -> Latn
+ {0xD3110000u, 38u}, // ryu -> Kana
+ {0x73610000u, 18u}, // sa -> Deva
+ {0x94120000u, 46u}, // saf -> Latn
+ {0x9C120000u, 17u}, // sah -> Cyrl
+ {0xC0120000u, 46u}, // saq -> Latn
+ {0xC8120000u, 46u}, // sas -> Latn
+ {0xCC120000u, 46u}, // sat -> Latn
+ {0xD4120000u, 46u}, // sav -> Latn
+ {0xE4120000u, 74u}, // saz -> Saur
+ {0x80320000u, 46u}, // sba -> Latn
+ {0x90320000u, 46u}, // sbe -> Latn
+ {0xBC320000u, 46u}, // sbp -> Latn
+ {0x73630000u, 46u}, // sc -> Latn
+ {0xA8520000u, 18u}, // sck -> Deva
{0xAC520000u, 1u}, // scl -> Arab
- {0xB4520000u, 44u}, // scn -> Latn
- {0xB8520000u, 44u}, // sco -> Latn
- {0xC8520000u, 44u}, // scs -> Latn
+ {0xB4520000u, 46u}, // scn -> Latn
+ {0xB8520000u, 46u}, // sco -> Latn
+ {0xC8520000u, 46u}, // scs -> Latn
{0x73640000u, 1u}, // sd -> Arab
- {0x88720000u, 44u}, // sdc -> Latn
+ {0x88720000u, 46u}, // sdc -> Latn
{0x9C720000u, 1u}, // sdh -> Arab
- {0x73650000u, 44u}, // se -> Latn
- {0x94920000u, 44u}, // sef -> Latn
- {0x9C920000u, 44u}, // seh -> Latn
- {0xA0920000u, 44u}, // sei -> Latn
- {0xC8920000u, 44u}, // ses -> Latn
- {0x73670000u, 44u}, // sg -> Latn
- {0x80D20000u, 60u}, // sga -> Ogam
- {0xC8D20000u, 44u}, // sgs -> Latn
- {0xD8D20000u, 19u}, // sgw -> Ethi
- {0xE4D20000u, 44u}, // sgz -> Latn
- {0x73680000u, 44u}, // sh -> Latn
- {0xA0F20000u, 85u}, // shi -> Tfng
- {0xA8F20000u, 44u}, // shk -> Latn
- {0xB4F20000u, 56u}, // shn -> Mymr
+ {0x73650000u, 46u}, // se -> Latn
+ {0x94920000u, 46u}, // sef -> Latn
+ {0x9C920000u, 46u}, // seh -> Latn
+ {0xA0920000u, 46u}, // sei -> Latn
+ {0xC8920000u, 46u}, // ses -> Latn
+ {0x73670000u, 46u}, // sg -> Latn
+ {0x80D20000u, 62u}, // sga -> Ogam
+ {0xC8D20000u, 46u}, // sgs -> Latn
+ {0xD8D20000u, 20u}, // sgw -> Ethi
+ {0xE4D20000u, 46u}, // sgz -> Latn
+ {0x73680000u, 46u}, // sh -> Latn
+ {0xA0F20000u, 87u}, // shi -> Tfng
+ {0xA8F20000u, 46u}, // shk -> Latn
+ {0xB4F20000u, 58u}, // shn -> Mymr
{0xD0F20000u, 1u}, // shu -> Arab
- {0x73690000u, 74u}, // si -> Sinh
- {0x8D120000u, 44u}, // sid -> Latn
- {0x99120000u, 44u}, // sig -> Latn
- {0xAD120000u, 44u}, // sil -> Latn
- {0xB1120000u, 44u}, // sim -> Latn
- {0xC5320000u, 44u}, // sjr -> Latn
- {0x736B0000u, 44u}, // sk -> Latn
- {0x89520000u, 44u}, // skc -> Latn
+ {0x73690000u, 76u}, // si -> Sinh
+ {0x8D120000u, 46u}, // sid -> Latn
+ {0x99120000u, 46u}, // sig -> Latn
+ {0xAD120000u, 46u}, // sil -> Latn
+ {0xB1120000u, 46u}, // sim -> Latn
+ {0xC5320000u, 46u}, // sjr -> Latn
+ {0x736B0000u, 46u}, // sk -> Latn
+ {0x89520000u, 46u}, // skc -> Latn
{0xC5520000u, 1u}, // skr -> Arab
- {0xC9520000u, 44u}, // sks -> Latn
- {0x736C0000u, 44u}, // sl -> Latn
- {0x8D720000u, 44u}, // sld -> Latn
- {0xA1720000u, 44u}, // sli -> Latn
- {0xAD720000u, 44u}, // sll -> Latn
- {0xE1720000u, 44u}, // sly -> Latn
- {0x736D0000u, 44u}, // sm -> Latn
- {0x81920000u, 44u}, // sma -> Latn
- {0xA5920000u, 44u}, // smj -> Latn
- {0xB5920000u, 44u}, // smn -> Latn
- {0xBD920000u, 70u}, // smp -> Samr
- {0xC1920000u, 44u}, // smq -> Latn
- {0xC9920000u, 44u}, // sms -> Latn
- {0x736E0000u, 44u}, // sn -> Latn
- {0x89B20000u, 44u}, // snc -> Latn
- {0xA9B20000u, 44u}, // snk -> Latn
- {0xBDB20000u, 44u}, // snp -> Latn
- {0xDDB20000u, 44u}, // snx -> Latn
- {0xE1B20000u, 44u}, // sny -> Latn
- {0x736F0000u, 44u}, // so -> Latn
- {0x99D20000u, 75u}, // sog -> Sogd
- {0xA9D20000u, 44u}, // sok -> Latn
- {0xC1D20000u, 44u}, // soq -> Latn
- {0xD1D20000u, 87u}, // sou -> Thai
- {0xE1D20000u, 44u}, // soy -> Latn
- {0x8DF20000u, 44u}, // spd -> Latn
- {0xADF20000u, 44u}, // spl -> Latn
- {0xC9F20000u, 44u}, // sps -> Latn
- {0x73710000u, 44u}, // sq -> Latn
- {0x73720000u, 16u}, // sr -> Cyrl
- {0x73724D45u, 44u}, // sr-ME -> Latn
- {0x7372524Fu, 44u}, // sr-RO -> Latn
- {0x73725255u, 44u}, // sr-RU -> Latn
- {0x73725452u, 44u}, // sr-TR -> Latn
- {0x86320000u, 76u}, // srb -> Sora
- {0xB6320000u, 44u}, // srn -> Latn
- {0xC6320000u, 44u}, // srr -> Latn
- {0xDE320000u, 17u}, // srx -> Deva
- {0x73730000u, 44u}, // ss -> Latn
- {0x8E520000u, 44u}, // ssd -> Latn
- {0x9A520000u, 44u}, // ssg -> Latn
- {0xE2520000u, 44u}, // ssy -> Latn
- {0x73740000u, 44u}, // st -> Latn
- {0xAA720000u, 44u}, // stk -> Latn
- {0xC2720000u, 44u}, // stq -> Latn
- {0x73750000u, 44u}, // su -> Latn
- {0x82920000u, 44u}, // sua -> Latn
- {0x92920000u, 44u}, // sue -> Latn
- {0xAA920000u, 44u}, // suk -> Latn
- {0xC6920000u, 44u}, // sur -> Latn
- {0xCA920000u, 44u}, // sus -> Latn
- {0x73760000u, 44u}, // sv -> Latn
- {0x73770000u, 44u}, // sw -> Latn
+ {0xC9520000u, 46u}, // sks -> Latn
+ {0x736C0000u, 46u}, // sl -> Latn
+ {0x8D720000u, 46u}, // sld -> Latn
+ {0xA1720000u, 46u}, // sli -> Latn
+ {0xAD720000u, 46u}, // sll -> Latn
+ {0xE1720000u, 46u}, // sly -> Latn
+ {0x736D0000u, 46u}, // sm -> Latn
+ {0x81920000u, 46u}, // sma -> Latn
+ {0xA5920000u, 46u}, // smj -> Latn
+ {0xB5920000u, 46u}, // smn -> Latn
+ {0xBD920000u, 72u}, // smp -> Samr
+ {0xC1920000u, 46u}, // smq -> Latn
+ {0xC9920000u, 46u}, // sms -> Latn
+ {0x736E0000u, 46u}, // sn -> Latn
+ {0x89B20000u, 46u}, // snc -> Latn
+ {0xA9B20000u, 46u}, // snk -> Latn
+ {0xBDB20000u, 46u}, // snp -> Latn
+ {0xDDB20000u, 46u}, // snx -> Latn
+ {0xE1B20000u, 46u}, // sny -> Latn
+ {0x736F0000u, 46u}, // so -> Latn
+ {0x99D20000u, 77u}, // sog -> Sogd
+ {0xA9D20000u, 46u}, // sok -> Latn
+ {0xC1D20000u, 46u}, // soq -> Latn
+ {0xD1D20000u, 89u}, // sou -> Thai
+ {0xE1D20000u, 46u}, // soy -> Latn
+ {0x8DF20000u, 46u}, // spd -> Latn
+ {0xADF20000u, 46u}, // spl -> Latn
+ {0xC9F20000u, 46u}, // sps -> Latn
+ {0x73710000u, 46u}, // sq -> Latn
+ {0x73720000u, 17u}, // sr -> Cyrl
+ {0x73724D45u, 46u}, // sr-ME -> Latn
+ {0x7372524Fu, 46u}, // sr-RO -> Latn
+ {0x73725255u, 46u}, // sr-RU -> Latn
+ {0x73725452u, 46u}, // sr-TR -> Latn
+ {0x86320000u, 78u}, // srb -> Sora
+ {0xB6320000u, 46u}, // srn -> Latn
+ {0xC6320000u, 46u}, // srr -> Latn
+ {0xDE320000u, 18u}, // srx -> Deva
+ {0x73730000u, 46u}, // ss -> Latn
+ {0x8E520000u, 46u}, // ssd -> Latn
+ {0x9A520000u, 46u}, // ssg -> Latn
+ {0xE2520000u, 46u}, // ssy -> Latn
+ {0x73740000u, 46u}, // st -> Latn
+ {0xAA720000u, 46u}, // stk -> Latn
+ {0xC2720000u, 46u}, // stq -> Latn
+ {0x73750000u, 46u}, // su -> Latn
+ {0x82920000u, 46u}, // sua -> Latn
+ {0x92920000u, 46u}, // sue -> Latn
+ {0xAA920000u, 46u}, // suk -> Latn
+ {0xC6920000u, 46u}, // sur -> Latn
+ {0xCA920000u, 46u}, // sus -> Latn
+ {0x73760000u, 46u}, // sv -> Latn
+ {0x73770000u, 46u}, // sw -> Latn
{0x86D20000u, 1u}, // swb -> Arab
- {0x8AD20000u, 44u}, // swc -> Latn
- {0x9AD20000u, 44u}, // swg -> Latn
- {0xBED20000u, 44u}, // swp -> Latn
- {0xD6D20000u, 17u}, // swv -> Deva
- {0xB6F20000u, 44u}, // sxn -> Latn
- {0xDAF20000u, 44u}, // sxw -> Latn
+ {0x8AD20000u, 46u}, // swc -> Latn
+ {0x9AD20000u, 46u}, // swg -> Latn
+ {0xBED20000u, 46u}, // swp -> Latn
+ {0xD6D20000u, 18u}, // swv -> Deva
+ {0xB6F20000u, 46u}, // sxn -> Latn
+ {0xDAF20000u, 46u}, // sxw -> Latn
{0xAF120000u, 7u}, // syl -> Beng
- {0xC7120000u, 78u}, // syr -> Syrc
- {0xAF320000u, 44u}, // szl -> Latn
- {0x74610000u, 81u}, // ta -> Taml
- {0xA4130000u, 17u}, // taj -> Deva
- {0xAC130000u, 44u}, // tal -> Latn
- {0xB4130000u, 44u}, // tan -> Latn
- {0xC0130000u, 44u}, // taq -> Latn
- {0x88330000u, 44u}, // tbc -> Latn
- {0x8C330000u, 44u}, // tbd -> Latn
- {0x94330000u, 44u}, // tbf -> Latn
- {0x98330000u, 44u}, // tbg -> Latn
- {0xB8330000u, 44u}, // tbo -> Latn
- {0xD8330000u, 44u}, // tbw -> Latn
- {0xE4330000u, 44u}, // tbz -> Latn
- {0xA0530000u, 44u}, // tci -> Latn
- {0xE0530000u, 40u}, // tcy -> Knda
- {0x8C730000u, 79u}, // tdd -> Tale
- {0x98730000u, 17u}, // tdg -> Deva
- {0x9C730000u, 17u}, // tdh -> Deva
- {0xD0730000u, 44u}, // tdu -> Latn
- {0x74650000u, 84u}, // te -> Telu
- {0x8C930000u, 44u}, // ted -> Latn
- {0xB0930000u, 44u}, // tem -> Latn
- {0xB8930000u, 44u}, // teo -> Latn
- {0xCC930000u, 44u}, // tet -> Latn
- {0xA0B30000u, 44u}, // tfi -> Latn
- {0x74670000u, 16u}, // tg -> Cyrl
+ {0xC7120000u, 80u}, // syr -> Syrc
+ {0xAF320000u, 46u}, // szl -> Latn
+ {0x74610000u, 83u}, // ta -> Taml
+ {0xA4130000u, 18u}, // taj -> Deva
+ {0xAC130000u, 46u}, // tal -> Latn
+ {0xB4130000u, 46u}, // tan -> Latn
+ {0xC0130000u, 46u}, // taq -> Latn
+ {0x88330000u, 46u}, // tbc -> Latn
+ {0x8C330000u, 46u}, // tbd -> Latn
+ {0x94330000u, 46u}, // tbf -> Latn
+ {0x98330000u, 46u}, // tbg -> Latn
+ {0xB8330000u, 46u}, // tbo -> Latn
+ {0xD8330000u, 46u}, // tbw -> Latn
+ {0xE4330000u, 46u}, // tbz -> Latn
+ {0xA0530000u, 46u}, // tci -> Latn
+ {0xE0530000u, 42u}, // tcy -> Knda
+ {0x8C730000u, 81u}, // tdd -> Tale
+ {0x98730000u, 18u}, // tdg -> Deva
+ {0x9C730000u, 18u}, // tdh -> Deva
+ {0xD0730000u, 46u}, // tdu -> Latn
+ {0x74650000u, 86u}, // te -> Telu
+ {0x8C930000u, 46u}, // ted -> Latn
+ {0xB0930000u, 46u}, // tem -> Latn
+ {0xB8930000u, 46u}, // teo -> Latn
+ {0xCC930000u, 46u}, // tet -> Latn
+ {0xA0B30000u, 46u}, // tfi -> Latn
+ {0x74670000u, 17u}, // tg -> Cyrl
{0x7467504Bu, 1u}, // tg-PK -> Arab
- {0x88D30000u, 44u}, // tgc -> Latn
- {0xB8D30000u, 44u}, // tgo -> Latn
- {0xD0D30000u, 44u}, // tgu -> Latn
- {0x74680000u, 87u}, // th -> Thai
- {0xACF30000u, 17u}, // thl -> Deva
- {0xC0F30000u, 17u}, // thq -> Deva
- {0xC4F30000u, 17u}, // thr -> Deva
- {0x74690000u, 19u}, // ti -> Ethi
- {0x95130000u, 44u}, // tif -> Latn
- {0x99130000u, 19u}, // tig -> Ethi
- {0xA9130000u, 44u}, // tik -> Latn
- {0xB1130000u, 44u}, // tim -> Latn
- {0xB9130000u, 44u}, // tio -> Latn
- {0xD5130000u, 44u}, // tiv -> Latn
- {0x746B0000u, 44u}, // tk -> Latn
- {0xAD530000u, 44u}, // tkl -> Latn
- {0xC5530000u, 44u}, // tkr -> Latn
- {0xCD530000u, 17u}, // tkt -> Deva
- {0x746C0000u, 44u}, // tl -> Latn
- {0x95730000u, 44u}, // tlf -> Latn
- {0xDD730000u, 44u}, // tlx -> Latn
- {0xE1730000u, 44u}, // tly -> Latn
- {0x9D930000u, 44u}, // tmh -> Latn
- {0xE1930000u, 44u}, // tmy -> Latn
- {0x746E0000u, 44u}, // tn -> Latn
- {0x9DB30000u, 44u}, // tnh -> Latn
- {0x746F0000u, 44u}, // to -> Latn
- {0x95D30000u, 44u}, // tof -> Latn
- {0x99D30000u, 44u}, // tog -> Latn
- {0xC1D30000u, 44u}, // toq -> Latn
- {0xA1F30000u, 44u}, // tpi -> Latn
- {0xB1F30000u, 44u}, // tpm -> Latn
- {0xE5F30000u, 44u}, // tpz -> Latn
- {0xBA130000u, 44u}, // tqo -> Latn
- {0x74720000u, 44u}, // tr -> Latn
- {0xD2330000u, 44u}, // tru -> Latn
- {0xD6330000u, 44u}, // trv -> Latn
+ {0x88D30000u, 46u}, // tgc -> Latn
+ {0xB8D30000u, 46u}, // tgo -> Latn
+ {0xD0D30000u, 46u}, // tgu -> Latn
+ {0x74680000u, 89u}, // th -> Thai
+ {0xACF30000u, 18u}, // thl -> Deva
+ {0xC0F30000u, 18u}, // thq -> Deva
+ {0xC4F30000u, 18u}, // thr -> Deva
+ {0x74690000u, 20u}, // ti -> Ethi
+ {0x95130000u, 46u}, // tif -> Latn
+ {0x99130000u, 20u}, // tig -> Ethi
+ {0xA9130000u, 46u}, // tik -> Latn
+ {0xB1130000u, 46u}, // tim -> Latn
+ {0xB9130000u, 46u}, // tio -> Latn
+ {0xD5130000u, 46u}, // tiv -> Latn
+ {0x746B0000u, 46u}, // tk -> Latn
+ {0xAD530000u, 46u}, // tkl -> Latn
+ {0xC5530000u, 46u}, // tkr -> Latn
+ {0xCD530000u, 18u}, // tkt -> Deva
+ {0x746C0000u, 46u}, // tl -> Latn
+ {0x95730000u, 46u}, // tlf -> Latn
+ {0xDD730000u, 46u}, // tlx -> Latn
+ {0xE1730000u, 46u}, // tly -> Latn
+ {0x9D930000u, 46u}, // tmh -> Latn
+ {0xE1930000u, 46u}, // tmy -> Latn
+ {0x746E0000u, 46u}, // tn -> Latn
+ {0x9DB30000u, 46u}, // tnh -> Latn
+ {0x746F0000u, 46u}, // to -> Latn
+ {0x95D30000u, 46u}, // tof -> Latn
+ {0x99D30000u, 46u}, // tog -> Latn
+ {0xC1D30000u, 46u}, // toq -> Latn
+ {0xA1F30000u, 46u}, // tpi -> Latn
+ {0xB1F30000u, 46u}, // tpm -> Latn
+ {0xE5F30000u, 46u}, // tpz -> Latn
+ {0xBA130000u, 46u}, // tqo -> Latn
+ {0x74720000u, 46u}, // tr -> Latn
+ {0xD2330000u, 46u}, // tru -> Latn
+ {0xD6330000u, 46u}, // trv -> Latn
{0xDA330000u, 1u}, // trw -> Arab
- {0x74730000u, 44u}, // ts -> Latn
- {0x8E530000u, 24u}, // tsd -> Grek
- {0x96530000u, 17u}, // tsf -> Deva
- {0x9A530000u, 44u}, // tsg -> Latn
- {0xA6530000u, 88u}, // tsj -> Tibt
- {0xDA530000u, 44u}, // tsw -> Latn
- {0x74740000u, 16u}, // tt -> Cyrl
- {0x8E730000u, 44u}, // ttd -> Latn
- {0x92730000u, 44u}, // tte -> Latn
- {0xA6730000u, 44u}, // ttj -> Latn
- {0xC6730000u, 44u}, // ttr -> Latn
- {0xCA730000u, 87u}, // tts -> Thai
- {0xCE730000u, 44u}, // ttt -> Latn
- {0x9E930000u, 44u}, // tuh -> Latn
- {0xAE930000u, 44u}, // tul -> Latn
- {0xB2930000u, 44u}, // tum -> Latn
- {0xC2930000u, 44u}, // tuq -> Latn
- {0x8EB30000u, 44u}, // tvd -> Latn
- {0xAEB30000u, 44u}, // tvl -> Latn
- {0xD2B30000u, 44u}, // tvu -> Latn
- {0x9ED30000u, 44u}, // twh -> Latn
- {0xC2D30000u, 44u}, // twq -> Latn
- {0x9AF30000u, 82u}, // txg -> Tang
- {0x74790000u, 44u}, // ty -> Latn
- {0x83130000u, 44u}, // tya -> Latn
- {0xD7130000u, 16u}, // tyv -> Cyrl
- {0xB3330000u, 44u}, // tzm -> Latn
- {0xD0340000u, 44u}, // ubu -> Latn
- {0xB0740000u, 16u}, // udm -> Cyrl
+ {0x74730000u, 46u}, // ts -> Latn
+ {0x8E530000u, 25u}, // tsd -> Grek
+ {0x96530000u, 18u}, // tsf -> Deva
+ {0x9A530000u, 46u}, // tsg -> Latn
+ {0xA6530000u, 90u}, // tsj -> Tibt
+ {0xDA530000u, 46u}, // tsw -> Latn
+ {0x74740000u, 17u}, // tt -> Cyrl
+ {0x8E730000u, 46u}, // ttd -> Latn
+ {0x92730000u, 46u}, // tte -> Latn
+ {0xA6730000u, 46u}, // ttj -> Latn
+ {0xC6730000u, 46u}, // ttr -> Latn
+ {0xCA730000u, 89u}, // tts -> Thai
+ {0xCE730000u, 46u}, // ttt -> Latn
+ {0x9E930000u, 46u}, // tuh -> Latn
+ {0xAE930000u, 46u}, // tul -> Latn
+ {0xB2930000u, 46u}, // tum -> Latn
+ {0xC2930000u, 46u}, // tuq -> Latn
+ {0x8EB30000u, 46u}, // tvd -> Latn
+ {0xAEB30000u, 46u}, // tvl -> Latn
+ {0xD2B30000u, 46u}, // tvu -> Latn
+ {0x9ED30000u, 46u}, // twh -> Latn
+ {0xC2D30000u, 46u}, // twq -> Latn
+ {0x9AF30000u, 84u}, // txg -> Tang
+ {0x74790000u, 46u}, // ty -> Latn
+ {0x83130000u, 46u}, // tya -> Latn
+ {0xD7130000u, 17u}, // tyv -> Cyrl
+ {0xB3330000u, 46u}, // tzm -> Latn
+ {0xD0340000u, 46u}, // ubu -> Latn
+ {0xB0740000u, 17u}, // udm -> Cyrl
{0x75670000u, 1u}, // ug -> Arab
- {0x75674B5Au, 16u}, // ug-KZ -> Cyrl
- {0x75674D4Eu, 16u}, // ug-MN -> Cyrl
- {0x80D40000u, 89u}, // uga -> Ugar
- {0x756B0000u, 16u}, // uk -> Cyrl
- {0xA1740000u, 44u}, // uli -> Latn
- {0x85940000u, 44u}, // umb -> Latn
+ {0x75674B5Au, 17u}, // ug-KZ -> Cyrl
+ {0x75674D4Eu, 17u}, // ug-MN -> Cyrl
+ {0x80D40000u, 91u}, // uga -> Ugar
+ {0x756B0000u, 17u}, // uk -> Cyrl
+ {0xA1740000u, 46u}, // uli -> Latn
+ {0x85940000u, 46u}, // umb -> Latn
{0xC5B40000u, 7u}, // unr -> Beng
- {0xC5B44E50u, 17u}, // unr-NP -> Deva
+ {0xC5B44E50u, 18u}, // unr-NP -> Deva
{0xDDB40000u, 7u}, // unx -> Beng
- {0xA9D40000u, 44u}, // uok -> Latn
+ {0xA9D40000u, 46u}, // uok -> Latn
{0x75720000u, 1u}, // ur -> Arab
- {0xA2340000u, 44u}, // uri -> Latn
- {0xCE340000u, 44u}, // urt -> Latn
- {0xDA340000u, 44u}, // urw -> Latn
- {0x82540000u, 44u}, // usa -> Latn
- {0xC6740000u, 44u}, // utr -> Latn
- {0x9EB40000u, 44u}, // uvh -> Latn
- {0xAEB40000u, 44u}, // uvl -> Latn
- {0x757A0000u, 44u}, // uz -> Latn
+ {0xA2340000u, 46u}, // uri -> Latn
+ {0xCE340000u, 46u}, // urt -> Latn
+ {0xDA340000u, 46u}, // urw -> Latn
+ {0x82540000u, 46u}, // usa -> Latn
+ {0xC6740000u, 46u}, // utr -> Latn
+ {0x9EB40000u, 46u}, // uvh -> Latn
+ {0xAEB40000u, 46u}, // uvl -> Latn
+ {0x757A0000u, 46u}, // uz -> Latn
{0x757A4146u, 1u}, // uz-AF -> Arab
- {0x757A434Eu, 16u}, // uz-CN -> Cyrl
- {0x98150000u, 44u}, // vag -> Latn
- {0xA0150000u, 90u}, // vai -> Vaii
- {0xB4150000u, 44u}, // van -> Latn
- {0x76650000u, 44u}, // ve -> Latn
- {0x88950000u, 44u}, // vec -> Latn
- {0xBC950000u, 44u}, // vep -> Latn
- {0x76690000u, 44u}, // vi -> Latn
- {0x89150000u, 44u}, // vic -> Latn
- {0xD5150000u, 44u}, // viv -> Latn
- {0xC9750000u, 44u}, // vls -> Latn
- {0x95950000u, 44u}, // vmf -> Latn
- {0xD9950000u, 44u}, // vmw -> Latn
- {0x766F0000u, 44u}, // vo -> Latn
- {0xCDD50000u, 44u}, // vot -> Latn
- {0xBA350000u, 44u}, // vro -> Latn
- {0xB6950000u, 44u}, // vun -> Latn
- {0xCE950000u, 44u}, // vut -> Latn
- {0x77610000u, 44u}, // wa -> Latn
- {0x90160000u, 44u}, // wae -> Latn
- {0xA4160000u, 44u}, // waj -> Latn
- {0xAC160000u, 19u}, // wal -> Ethi
- {0xB4160000u, 44u}, // wan -> Latn
- {0xC4160000u, 44u}, // war -> Latn
- {0xBC360000u, 44u}, // wbp -> Latn
- {0xC0360000u, 84u}, // wbq -> Telu
- {0xC4360000u, 17u}, // wbr -> Deva
- {0xA0560000u, 44u}, // wci -> Latn
- {0xC4960000u, 44u}, // wer -> Latn
- {0xA0D60000u, 44u}, // wgi -> Latn
- {0x98F60000u, 44u}, // whg -> Latn
- {0x85160000u, 44u}, // wib -> Latn
- {0xD1160000u, 44u}, // wiu -> Latn
- {0xD5160000u, 44u}, // wiv -> Latn
- {0x81360000u, 44u}, // wja -> Latn
- {0xA1360000u, 44u}, // wji -> Latn
- {0xC9760000u, 44u}, // wls -> Latn
- {0xB9960000u, 44u}, // wmo -> Latn
- {0x89B60000u, 44u}, // wnc -> Latn
+ {0x757A434Eu, 17u}, // uz-CN -> Cyrl
+ {0x98150000u, 46u}, // vag -> Latn
+ {0xA0150000u, 92u}, // vai -> Vaii
+ {0xB4150000u, 46u}, // van -> Latn
+ {0x76650000u, 46u}, // ve -> Latn
+ {0x88950000u, 46u}, // vec -> Latn
+ {0xBC950000u, 46u}, // vep -> Latn
+ {0x76690000u, 46u}, // vi -> Latn
+ {0x89150000u, 46u}, // vic -> Latn
+ {0xD5150000u, 46u}, // viv -> Latn
+ {0xC9750000u, 46u}, // vls -> Latn
+ {0x95950000u, 46u}, // vmf -> Latn
+ {0xD9950000u, 46u}, // vmw -> Latn
+ {0x766F0000u, 46u}, // vo -> Latn
+ {0xCDD50000u, 46u}, // vot -> Latn
+ {0xBA350000u, 46u}, // vro -> Latn
+ {0xB6950000u, 46u}, // vun -> Latn
+ {0xCE950000u, 46u}, // vut -> Latn
+ {0x77610000u, 46u}, // wa -> Latn
+ {0x90160000u, 46u}, // wae -> Latn
+ {0xA4160000u, 46u}, // waj -> Latn
+ {0xAC160000u, 20u}, // wal -> Ethi
+ {0xB4160000u, 46u}, // wan -> Latn
+ {0xC4160000u, 46u}, // war -> Latn
+ {0xBC360000u, 46u}, // wbp -> Latn
+ {0xC0360000u, 86u}, // wbq -> Telu
+ {0xC4360000u, 18u}, // wbr -> Deva
+ {0xA0560000u, 46u}, // wci -> Latn
+ {0xC4960000u, 46u}, // wer -> Latn
+ {0xA0D60000u, 46u}, // wgi -> Latn
+ {0x98F60000u, 46u}, // whg -> Latn
+ {0x85160000u, 46u}, // wib -> Latn
+ {0xD1160000u, 46u}, // wiu -> Latn
+ {0xD5160000u, 46u}, // wiv -> Latn
+ {0x81360000u, 46u}, // wja -> Latn
+ {0xA1360000u, 46u}, // wji -> Latn
+ {0xC9760000u, 46u}, // wls -> Latn
+ {0xB9960000u, 46u}, // wmo -> Latn
+ {0x89B60000u, 46u}, // wnc -> Latn
{0xA1B60000u, 1u}, // wni -> Arab
- {0xD1B60000u, 44u}, // wnu -> Latn
- {0x776F0000u, 44u}, // wo -> Latn
- {0x85D60000u, 44u}, // wob -> Latn
- {0xC9D60000u, 44u}, // wos -> Latn
- {0xCA360000u, 44u}, // wrs -> Latn
- {0x9A560000u, 21u}, // wsg -> Gong
- {0xAA560000u, 44u}, // wsk -> Latn
- {0xB2760000u, 17u}, // wtm -> Deva
- {0xD2960000u, 27u}, // wuu -> Hans
- {0xD6960000u, 44u}, // wuv -> Latn
- {0x82D60000u, 44u}, // wwa -> Latn
- {0xD4170000u, 44u}, // xav -> Latn
- {0xA0370000u, 44u}, // xbi -> Latn
+ {0xD1B60000u, 46u}, // wnu -> Latn
+ {0x776F0000u, 46u}, // wo -> Latn
+ {0x85D60000u, 46u}, // wob -> Latn
+ {0xC9D60000u, 46u}, // wos -> Latn
+ {0xCA360000u, 46u}, // wrs -> Latn
+ {0x9A560000u, 22u}, // wsg -> Gong
+ {0xAA560000u, 46u}, // wsk -> Latn
+ {0xB2760000u, 18u}, // wtm -> Deva
+ {0xD2960000u, 28u}, // wuu -> Hans
+ {0xD6960000u, 46u}, // wuv -> Latn
+ {0x82D60000u, 46u}, // wwa -> Latn
+ {0xD4170000u, 46u}, // xav -> Latn
+ {0xA0370000u, 46u}, // xbi -> Latn
+ {0xB8570000u, 14u}, // xco -> Chrs
{0xC4570000u, 11u}, // xcr -> Cari
- {0xC8970000u, 44u}, // xes -> Latn
- {0x78680000u, 44u}, // xh -> Latn
- {0x81770000u, 44u}, // xla -> Latn
- {0x89770000u, 48u}, // xlc -> Lyci
- {0x8D770000u, 49u}, // xld -> Lydi
- {0x95970000u, 20u}, // xmf -> Geor
- {0xB5970000u, 51u}, // xmn -> Mani
- {0xC5970000u, 52u}, // xmr -> Merc
- {0x81B70000u, 57u}, // xna -> Narb
- {0xC5B70000u, 17u}, // xnr -> Deva
- {0x99D70000u, 44u}, // xog -> Latn
- {0xB5D70000u, 44u}, // xon -> Latn
- {0xC5F70000u, 68u}, // xpr -> Prti
- {0x86370000u, 44u}, // xrb -> Latn
- {0x82570000u, 71u}, // xsa -> Sarb
- {0xA2570000u, 44u}, // xsi -> Latn
- {0xB2570000u, 44u}, // xsm -> Latn
- {0xC6570000u, 17u}, // xsr -> Deva
- {0x92D70000u, 44u}, // xwe -> Latn
- {0xB0180000u, 44u}, // yam -> Latn
- {0xB8180000u, 44u}, // yao -> Latn
- {0xBC180000u, 44u}, // yap -> Latn
- {0xC8180000u, 44u}, // yas -> Latn
- {0xCC180000u, 44u}, // yat -> Latn
- {0xD4180000u, 44u}, // yav -> Latn
- {0xE0180000u, 44u}, // yay -> Latn
- {0xE4180000u, 44u}, // yaz -> Latn
- {0x80380000u, 44u}, // yba -> Latn
- {0x84380000u, 44u}, // ybb -> Latn
- {0xE0380000u, 44u}, // yby -> Latn
- {0xC4980000u, 44u}, // yer -> Latn
- {0xC4D80000u, 44u}, // ygr -> Latn
- {0xD8D80000u, 44u}, // ygw -> Latn
- {0x79690000u, 30u}, // yi -> Hebr
- {0xB9580000u, 44u}, // yko -> Latn
- {0x91780000u, 44u}, // yle -> Latn
- {0x99780000u, 44u}, // ylg -> Latn
- {0xAD780000u, 44u}, // yll -> Latn
- {0xAD980000u, 44u}, // yml -> Latn
- {0x796F0000u, 44u}, // yo -> Latn
- {0xB5D80000u, 44u}, // yon -> Latn
- {0x86380000u, 44u}, // yrb -> Latn
- {0x92380000u, 44u}, // yre -> Latn
- {0xAE380000u, 44u}, // yrl -> Latn
- {0xCA580000u, 44u}, // yss -> Latn
- {0x82980000u, 44u}, // yua -> Latn
- {0x92980000u, 28u}, // yue -> Hant
- {0x9298434Eu, 27u}, // yue-CN -> Hans
- {0xA6980000u, 44u}, // yuj -> Latn
- {0xCE980000u, 44u}, // yut -> Latn
- {0xDA980000u, 44u}, // yuw -> Latn
- {0x7A610000u, 44u}, // za -> Latn
- {0x98190000u, 44u}, // zag -> Latn
+ {0xC8970000u, 46u}, // xes -> Latn
+ {0x78680000u, 46u}, // xh -> Latn
+ {0x81770000u, 46u}, // xla -> Latn
+ {0x89770000u, 50u}, // xlc -> Lyci
+ {0x8D770000u, 51u}, // xld -> Lydi
+ {0x95970000u, 21u}, // xmf -> Geor
+ {0xB5970000u, 53u}, // xmn -> Mani
+ {0xC5970000u, 54u}, // xmr -> Merc
+ {0x81B70000u, 59u}, // xna -> Narb
+ {0xC5B70000u, 18u}, // xnr -> Deva
+ {0x99D70000u, 46u}, // xog -> Latn
+ {0xB5D70000u, 46u}, // xon -> Latn
+ {0xC5F70000u, 70u}, // xpr -> Prti
+ {0x86370000u, 46u}, // xrb -> Latn
+ {0x82570000u, 73u}, // xsa -> Sarb
+ {0xA2570000u, 46u}, // xsi -> Latn
+ {0xB2570000u, 46u}, // xsm -> Latn
+ {0xC6570000u, 18u}, // xsr -> Deva
+ {0x92D70000u, 46u}, // xwe -> Latn
+ {0xB0180000u, 46u}, // yam -> Latn
+ {0xB8180000u, 46u}, // yao -> Latn
+ {0xBC180000u, 46u}, // yap -> Latn
+ {0xC8180000u, 46u}, // yas -> Latn
+ {0xCC180000u, 46u}, // yat -> Latn
+ {0xD4180000u, 46u}, // yav -> Latn
+ {0xE0180000u, 46u}, // yay -> Latn
+ {0xE4180000u, 46u}, // yaz -> Latn
+ {0x80380000u, 46u}, // yba -> Latn
+ {0x84380000u, 46u}, // ybb -> Latn
+ {0xE0380000u, 46u}, // yby -> Latn
+ {0xC4980000u, 46u}, // yer -> Latn
+ {0xC4D80000u, 46u}, // ygr -> Latn
+ {0xD8D80000u, 46u}, // ygw -> Latn
+ {0x79690000u, 31u}, // yi -> Hebr
+ {0xB9580000u, 46u}, // yko -> Latn
+ {0x91780000u, 46u}, // yle -> Latn
+ {0x99780000u, 46u}, // ylg -> Latn
+ {0xAD780000u, 46u}, // yll -> Latn
+ {0xAD980000u, 46u}, // yml -> Latn
+ {0x796F0000u, 46u}, // yo -> Latn
+ {0xB5D80000u, 46u}, // yon -> Latn
+ {0x86380000u, 46u}, // yrb -> Latn
+ {0x92380000u, 46u}, // yre -> Latn
+ {0xAE380000u, 46u}, // yrl -> Latn
+ {0xCA580000u, 46u}, // yss -> Latn
+ {0x82980000u, 46u}, // yua -> Latn
+ {0x92980000u, 29u}, // yue -> Hant
+ {0x9298434Eu, 28u}, // yue-CN -> Hans
+ {0xA6980000u, 46u}, // yuj -> Latn
+ {0xCE980000u, 46u}, // yut -> Latn
+ {0xDA980000u, 46u}, // yuw -> Latn
+ {0x7A610000u, 46u}, // za -> Latn
+ {0x98190000u, 46u}, // zag -> Latn
{0xA4790000u, 1u}, // zdj -> Arab
- {0x80990000u, 44u}, // zea -> Latn
- {0x9CD90000u, 85u}, // zgh -> Tfng
- {0x7A680000u, 27u}, // zh -> Hans
- {0x7A684155u, 28u}, // zh-AU -> Hant
- {0x7A68424Eu, 28u}, // zh-BN -> Hant
- {0x7A684742u, 28u}, // zh-GB -> Hant
- {0x7A684746u, 28u}, // zh-GF -> Hant
- {0x7A68484Bu, 28u}, // zh-HK -> Hant
- {0x7A684944u, 28u}, // zh-ID -> Hant
- {0x7A684D4Fu, 28u}, // zh-MO -> Hant
- {0x7A684D59u, 28u}, // zh-MY -> Hant
- {0x7A685041u, 28u}, // zh-PA -> Hant
- {0x7A685046u, 28u}, // zh-PF -> Hant
- {0x7A685048u, 28u}, // zh-PH -> Hant
- {0x7A685352u, 28u}, // zh-SR -> Hant
- {0x7A685448u, 28u}, // zh-TH -> Hant
- {0x7A685457u, 28u}, // zh-TW -> Hant
- {0x7A685553u, 28u}, // zh-US -> Hant
- {0x7A68564Eu, 28u}, // zh-VN -> Hant
- {0xDCF90000u, 59u}, // zhx -> Nshu
- {0x81190000u, 44u}, // zia -> Latn
- {0xB1790000u, 44u}, // zlm -> Latn
- {0xA1990000u, 44u}, // zmi -> Latn
- {0x91B90000u, 44u}, // zne -> Latn
- {0x7A750000u, 44u}, // zu -> Latn
- {0x83390000u, 44u}, // zza -> Latn
+ {0x80990000u, 46u}, // zea -> Latn
+ {0x9CD90000u, 87u}, // zgh -> Tfng
+ {0x7A680000u, 28u}, // zh -> Hans
+ {0x7A684155u, 29u}, // zh-AU -> Hant
+ {0x7A68424Eu, 29u}, // zh-BN -> Hant
+ {0x7A684742u, 29u}, // zh-GB -> Hant
+ {0x7A684746u, 29u}, // zh-GF -> Hant
+ {0x7A68484Bu, 29u}, // zh-HK -> Hant
+ {0x7A684944u, 29u}, // zh-ID -> Hant
+ {0x7A684D4Fu, 29u}, // zh-MO -> Hant
+ {0x7A684D59u, 29u}, // zh-MY -> Hant
+ {0x7A685041u, 29u}, // zh-PA -> Hant
+ {0x7A685046u, 29u}, // zh-PF -> Hant
+ {0x7A685048u, 29u}, // zh-PH -> Hant
+ {0x7A685352u, 29u}, // zh-SR -> Hant
+ {0x7A685448u, 29u}, // zh-TH -> Hant
+ {0x7A685457u, 29u}, // zh-TW -> Hant
+ {0x7A685553u, 29u}, // zh-US -> Hant
+ {0x7A68564Eu, 29u}, // zh-VN -> Hant
+ {0xDCF90000u, 61u}, // zhx -> Nshu
+ {0x81190000u, 46u}, // zia -> Latn
+ {0xCD590000u, 41u}, // zkt -> Kits
+ {0xB1790000u, 46u}, // zlm -> Latn
+ {0xA1990000u, 46u}, // zmi -> Latn
+ {0x91B90000u, 46u}, // zne -> Latn
+ {0x7A750000u, 46u}, // zu -> Latn
+ {0x83390000u, 46u}, // zza -> Latn
});
std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -1829,6 +1833,7 @@
0xC66A4D594C61746ELLU, // ktr_Latn_MY
0x6B75495141726162LLU, // ku_Arab_IQ
0x6B7554524C61746ELLU, // ku_Latn_TR
+ 0x6B75474559657A69LLU, // ku_Yezi_GE
0xB28A52554379726CLLU, // kum_Cyrl_RU
0x6B7652554379726CLLU, // kv_Cyrl_RU
0xC6AA49444C61746ELLU, // kvr_Latn_ID
@@ -2199,6 +2204,7 @@
0xB276494E44657661LLU, // wtm_Deva_IN
0xD296434E48616E73LLU, // wuu_Hans_CN
0xD41742524C61746ELLU, // xav_Latn_BR
+ 0xB857555A43687273LLU, // xco_Chrs_UZ
0xC457545243617269LLU, // xcr_Cari_TR
0x78685A414C61746ELLU, // xh_Latn_ZA
0x897754524C796369LLU, // xlc_Lyci_TR
@@ -2231,6 +2237,7 @@
0x7A68434E48616E73LLU, // zh_Hans_CN
0x7A68545748616E74LLU, // zh_Hant_TW
0xDCF9434E4E736875LLU, // zhx_Nshu_CN
+ 0xCD59434E4B697473LLU, // zkt_Kits_CN
0xB17954474C61746ELLU, // zlm_Latn_TG
0xA1994D594C61746ELLU, // zmi_Latn_MY
0x7A755A414C61746ELLU, // zu_Latn_ZA
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 83a8995..5e3b8aa 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -21,8 +21,8 @@
import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_FREQUENCY;
import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_PHASE;
import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_PHASE_UNCERTAINTY;
-import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_RECEIVER_ISB;
-import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_RECEIVER_ISB_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_FULL_ISB;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_FULL_ISB_UNCERTAINTY;
import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SATELLITE_ISB;
import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SATELLITE_ISB_UNCERTAINTY;
import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SNR;
@@ -63,8 +63,8 @@
private double mSnrInDb;
private double mAutomaticGainControlLevelInDb;
@NonNull private String mCodeType;
- private double mReceiverInterSignalBiasNanos;
- private double mReceiverInterSignalBiasUncertaintyNanos;
+ private double mFullInterSignalBiasNanos;
+ private double mFullInterSignalBiasUncertaintyNanos;
private double mSatelliteInterSignalBiasNanos;
private double mSatelliteInterSignalBiasUncertaintyNanos;
@@ -268,9 +268,9 @@
mSnrInDb = measurement.mSnrInDb;
mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb;
mCodeType = measurement.mCodeType;
- mReceiverInterSignalBiasNanos = measurement.mReceiverInterSignalBiasNanos;
- mReceiverInterSignalBiasUncertaintyNanos =
- measurement.mReceiverInterSignalBiasUncertaintyNanos;
+ mFullInterSignalBiasNanos = measurement.mFullInterSignalBiasNanos;
+ mFullInterSignalBiasUncertaintyNanos =
+ measurement.mFullInterSignalBiasUncertaintyNanos;
mSatelliteInterSignalBiasNanos = measurement.mSatelliteInterSignalBiasNanos;
mSatelliteInterSignalBiasUncertaintyNanos =
measurement.mSatelliteInterSignalBiasUncertaintyNanos;
@@ -1435,99 +1435,110 @@
}
/**
- * Returns {@code true} if {@link #getReceiverInterSignalBiasNanos()} is available,
+ * Returns {@code true} if {@link #getFullInterSignalBiasNanos()} is available,
* {@code false} otherwise.
*/
- public boolean hasReceiverInterSignalBiasNanos() {
- return isFlagSet(HAS_RECEIVER_ISB);
+ public boolean hasFullInterSignalBiasNanos() {
+ return isFlagSet(HAS_FULL_ISB);
}
/**
- * Gets the GNSS measurement's receiver inter-signal bias in nanoseconds with sub-nanosecond
- * accuracy.
+ * Gets the GNSS measurement's inter-signal bias in nanoseconds with sub-nanosecond accuracy.
*
- * <p>This value is the estimated receiver-side inter-system (different from the
- * constellation in {@link GnssClock#getReferenceConstellationTypeForIsb()} bias and
- * inter-frequency (different from the carrier frequency in
- * {@link GnssClock#getReferenceCarrierFrequencyHzForIsb()}) bias. The reported receiver
- * inter-signal bias must include signal delays caused by:
+ * <p>This value is the sum of the estimated receiver-side and the space-segment-side
+ * inter-system bias, inter-frequency bias and inter-code bias, including:
*
* <ul>
- * <li>Receiver inter-constellation bias</li>
- * <li>Receiver inter-frequency bias</li>
- * <li>Receiver inter-code bias</li>
+ * <li>Receiver inter-constellation bias (with respect to the constellation in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Receiver inter-frequency bias (with respect to the carrier frequency in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Receiver inter-code bias (with respect to the code type in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPS-UTC Time Offset (TauGps),
+ * BDS-GLO Time Offset (BGTO))(with respect to the constellation in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Group delay (e.g., Total Group Delay (TGD))</li>
+ * <li>Satellite inter-frequency bias (GLO only) (with respect to the carrier frequency in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the code
+ * type in {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
* </ul>
*
+ * <p>If a component of the above is already compensated in the provided
+ * {@link GnssMeasurement#getReceivedSvTimeNanos()}, then it must not be included in the
+ * reported full ISB.
+ *
* <p>The value does not include the inter-frequency Ionospheric bias.
*
- * <p>The value is only available if {@link #hasReceiverInterSignalBiasNanos()} is {@code true}.
+ * <p>The value is only available if {@link #hasFullInterSignalBiasNanos()} is {@code true}.
*/
- public double getReceiverInterSignalBiasNanos() {
- return mReceiverInterSignalBiasNanos;
+ public double getFullInterSignalBiasNanos() {
+ return mFullInterSignalBiasNanos;
}
/**
- * Sets the GNSS measurement's receiver inter-signal bias in nanoseconds.
+ * Sets the GNSS measurement's inter-signal bias in nanoseconds.
*
* @hide
*/
@TestApi
- public void setReceiverInterSignalBiasNanos(double receiverInterSignalBiasNanos) {
- setFlag(HAS_RECEIVER_ISB);
- mReceiverInterSignalBiasNanos = receiverInterSignalBiasNanos;
+ public void setFullInterSignalBiasNanos(double fullInterSignalBiasNanos) {
+ setFlag(HAS_FULL_ISB);
+ mFullInterSignalBiasNanos = fullInterSignalBiasNanos;
}
/**
- * Resets the GNSS measurement's receiver inter-signal bias in nanoseconds.
+ * Resets the GNSS measurement's inter-signal bias in nanoseconds.
*
* @hide
*/
@TestApi
- public void resetReceiverInterSignalBiasNanos() {
- resetFlag(HAS_RECEIVER_ISB);
+ public void resetFullInterSignalBiasNanos() {
+ resetFlag(HAS_FULL_ISB);
}
/**
- * Returns {@code true} if {@link #getReceiverInterSignalBiasUncertaintyNanos()} is available,
+ * Returns {@code true} if {@link #getFullInterSignalBiasUncertaintyNanos()} is available,
* {@code false} otherwise.
*/
- public boolean hasReceiverInterSignalBiasUncertaintyNanos() {
- return isFlagSet(HAS_RECEIVER_ISB_UNCERTAINTY);
+ public boolean hasFullInterSignalBiasUncertaintyNanos() {
+ return isFlagSet(HAS_FULL_ISB_UNCERTAINTY);
}
/**
- * Gets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in
+ * Gets the GNSS measurement's inter-signal bias uncertainty (1 sigma) in
* nanoseconds with sub-nanosecond accuracy.
*
- * <p>The value is only available if {@link #hasReceiverInterSignalBiasUncertaintyNanos()} is
+ * <p>The value is only available if {@link #hasFullInterSignalBiasUncertaintyNanos()} is
* {@code true}.
*/
@FloatRange(from = 0.0)
- public double getReceiverInterSignalBiasUncertaintyNanos() {
- return mReceiverInterSignalBiasUncertaintyNanos;
+ public double getFullInterSignalBiasUncertaintyNanos() {
+ return mFullInterSignalBiasUncertaintyNanos;
}
/**
- * Sets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in nanoseconds.
+ * Sets the GNSS measurement's inter-signal bias uncertainty (1 sigma) in nanoseconds.
*
* @hide
*/
@TestApi
- public void setReceiverInterSignalBiasUncertaintyNanos(@FloatRange(from = 0.0)
- double receiverInterSignalBiasUncertaintyNanos) {
- setFlag(HAS_RECEIVER_ISB_UNCERTAINTY);
- mReceiverInterSignalBiasUncertaintyNanos = receiverInterSignalBiasUncertaintyNanos;
+ public void setFullInterSignalBiasUncertaintyNanos(@FloatRange(from = 0.0)
+ double fullInterSignalBiasUncertaintyNanos) {
+ setFlag(HAS_FULL_ISB_UNCERTAINTY);
+ mFullInterSignalBiasUncertaintyNanos = fullInterSignalBiasUncertaintyNanos;
}
/**
- * Resets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in
+ * Resets the GNSS measurement's inter-signal bias uncertainty (1 sigma) in
* nanoseconds.
*
* @hide
*/
@TestApi
- public void resetReceiverInterSignalBiasUncertaintyNanos() {
- resetFlag(HAS_RECEIVER_ISB_UNCERTAINTY);
+ public void resetFullInterSignalBiasUncertaintyNanos() {
+ resetFlag(HAS_FULL_ISB_UNCERTAINTY);
}
/**
@@ -1542,17 +1553,18 @@
* Gets the GNSS measurement's satellite inter-signal bias in nanoseconds with sub-nanosecond
* accuracy.
*
- * <p>This value is the satellite-and-control-segment-side inter-system (different from the
- * constellation in {@link GnssClock#getReferenceConstellationTypeForIsb()}) bias and
- * inter-frequency (different from the carrier frequency in
- * {@link GnssClock#getReferenceCarrierFrequencyHzForIsb()}) bias, including:
+ * <p>This value is the space-segment-side inter-system bias, inter-frequency bias and
+ * inter-code bias, including:
*
* <ul>
- * <li>Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPT-UTC Time Offset (TauGps),
- * BDS-GLO Time Offset (BGTO))</li>
+ * <li>Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPS-UTC Time Offset (TauGps),
+ * BDS-GLO Time Offset (BGTO))(with respect to the constellation in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
* <li>Group delay (e.g., Total Group Delay (TGD))</li>
- * <li>Satellite inter-signal bias, which includes satellite inter-frequency bias (GLO only),
- * and satellite inter-code bias (e.g., Differential Code Bias (DCB)).</li>
+ * <li>Satellite inter-frequency bias (GLO only) (with respect to the carrier frequency in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the code
+ * type in {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
* </ul>
*
* <p>The value is only available if {@link #hasSatelliteInterSignalBiasNanos()} is {@code
@@ -1654,8 +1666,8 @@
gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble();
gnssMeasurement.mCodeType = parcel.readString();
gnssMeasurement.mBasebandCn0DbHz = parcel.readDouble();
- gnssMeasurement.mReceiverInterSignalBiasNanos = parcel.readDouble();
- gnssMeasurement.mReceiverInterSignalBiasUncertaintyNanos = parcel.readDouble();
+ gnssMeasurement.mFullInterSignalBiasNanos = parcel.readDouble();
+ gnssMeasurement.mFullInterSignalBiasUncertaintyNanos = parcel.readDouble();
gnssMeasurement.mSatelliteInterSignalBiasNanos = parcel.readDouble();
gnssMeasurement.mSatelliteInterSignalBiasUncertaintyNanos = parcel.readDouble();
@@ -1692,8 +1704,8 @@
parcel.writeDouble(mAutomaticGainControlLevelInDb);
parcel.writeString(mCodeType);
parcel.writeDouble(mBasebandCn0DbHz);
- parcel.writeDouble(mReceiverInterSignalBiasNanos);
- parcel.writeDouble(mReceiverInterSignalBiasUncertaintyNanos);
+ parcel.writeDouble(mFullInterSignalBiasNanos);
+ parcel.writeDouble(mFullInterSignalBiasUncertaintyNanos);
parcel.writeDouble(mSatelliteInterSignalBiasNanos);
parcel.writeDouble(mSatelliteInterSignalBiasUncertaintyNanos);
}
@@ -1778,14 +1790,14 @@
builder.append(String.format(format, "CodeType", mCodeType));
}
- if (hasReceiverInterSignalBiasNanos() || hasReceiverInterSignalBiasUncertaintyNanos()) {
+ if (hasFullInterSignalBiasNanos() || hasFullInterSignalBiasUncertaintyNanos()) {
builder.append(String.format(
formatWithUncertainty,
- "ReceiverInterSignalBiasNs",
- hasReceiverInterSignalBiasNanos() ? mReceiverInterSignalBiasNanos : null,
- "ReceiverInterSignalBiasUncertaintyNs",
- hasReceiverInterSignalBiasUncertaintyNanos()
- ? mReceiverInterSignalBiasUncertaintyNanos : null));
+ "InterSignalBiasNs",
+ hasFullInterSignalBiasNanos() ? mFullInterSignalBiasNanos : null,
+ "InterSignalBiasUncertaintyNs",
+ hasFullInterSignalBiasUncertaintyNanos()
+ ? mFullInterSignalBiasUncertaintyNanos : null));
}
if (hasSatelliteInterSignalBiasNanos() || hasSatelliteInterSignalBiasUncertaintyNanos()) {
@@ -1824,8 +1836,8 @@
resetAutomaticGainControlLevel();
resetCodeType();
resetBasebandCn0DbHz();
- resetReceiverInterSignalBiasNanos();
- resetReceiverInterSignalBiasUncertaintyNanos();
+ resetFullInterSignalBiasNanos();
+ resetFullInterSignalBiasUncertaintyNanos();
resetSatelliteInterSignalBiasNanos();
resetSatelliteInterSignalBiasUncertaintyNanos();
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 550e41f..ff7049e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -612,7 +612,7 @@
public Location getLastLocation() {
try {
return mService.getLastLocation(null, mContext.getPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -645,7 +645,7 @@
try {
return mService.getLastLocation(request, mContext.getPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -742,7 +742,7 @@
try {
if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
- listenerTransport, mContext.getPackageName(), mContext.getFeatureId(),
+ listenerTransport, mContext.getPackageName(), mContext.getAttributionTag(),
getListenerIdentifier(consumer))) {
listenerTransport.register(mContext.getSystemService(AlarmManager.class),
remoteCancellationSignal);
@@ -1189,7 +1189,7 @@
boolean registered = false;
try {
mService.requestLocationUpdates(locationRequest, transport, null,
- mContext.getPackageName(), mContext.getFeatureId(),
+ mContext.getPackageName(), mContext.getAttributionTag(),
getListenerIdentifier(listener));
registered = true;
} catch (RemoteException e) {
@@ -1235,7 +1235,7 @@
try {
mService.requestLocationUpdates(locationRequest, null, pendingIntent,
- mContext.getPackageName(), mContext.getFeatureId(),
+ mContext.getPackageName(), mContext.getAttributionTag(),
getListenerIdentifier(pendingIntent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1711,7 +1711,7 @@
LocationRequest request = new LocationRequest().setExpireIn(expiration);
try {
mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
- mContext.getFeatureId(), getListenerIdentifier(intent));
+ mContext.getAttributionTag(), getListenerIdentifier(intent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1798,7 +1798,7 @@
try {
mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
- mContext.getFeatureId(), getListenerIdentifier(intent));
+ mContext.getAttributionTag(), getListenerIdentifier(intent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2946,7 +2946,7 @@
GnssStatusListener transport = new GnssStatusListener();
if (mService.registerGnssStatusCallback(transport, mContext.getPackageName(),
- mContext.getFeatureId())) {
+ mContext.getAttributionTag())) {
mListenerTransport = transport;
return true;
} else {
@@ -3012,7 +3012,7 @@
GnssMeasurementsListener transport = new GnssMeasurementsListener();
if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
- mContext.getFeatureId(), "gnss measurement callback")) {
+ mContext.getAttributionTag(), "gnss measurement callback")) {
mListenerTransport = transport;
return true;
} else {
@@ -3065,7 +3065,7 @@
GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
- mContext.getFeatureId(), "gnss navigation callback")) {
+ mContext.getAttributionTag(), "gnss navigation callback")) {
mListenerTransport = transport;
return true;
} else {
@@ -3106,7 +3106,7 @@
GnssAntennaInfoListener transport = new GnssAntennaInfoListener();
if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
- mContext.getFeatureId(), "gnss antenna info callback")) {
+ mContext.getAttributionTag(), "gnss antenna info callback")) {
mListenerTransport = transport;
return true;
} else {
@@ -3143,7 +3143,7 @@
BatchedLocationCallback transport = new BatchedLocationCallback();
if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
- mContext.getFeatureId(), "batched location callback")) {
+ mContext.getAttributionTag(), "batched location callback")) {
mListenerTransport = transport;
return true;
} else {
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 3a771bb..ad9486c 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -870,16 +870,17 @@
private int getSessionResourceId() throws MediaCasException {
validateInternalStates();
- int[] sessionResourceId = new int[1];
- sessionResourceId[0] = -1;
+ int[] sessionResourceHandle = new int[1];
+ sessionResourceHandle[0] = -1;
if (mTunerResourceManager != null) {
CasSessionRequest casSessionRequest = new CasSessionRequest(mClientId, mCasSystemId);
- if (!mTunerResourceManager.requestCasSession(casSessionRequest, sessionResourceId)) {
- throw new MediaCasException.ResourceBusyException(
+ if (!mTunerResourceManager
+ .requestCasSession(casSessionRequest, sessionResourceHandle)) {
+ throw new MediaCasException.InsufficientResourceException(
"insufficient resource to Open Session");
}
}
- return sessionResourceId[0];
+ return sessionResourceHandle[0];
}
private void addSessionToResourceMap(Session session, int sessionResourceId) {
@@ -905,6 +906,10 @@
* Open a session to descramble one or more streams scrambled by the
* conditional access system.
*
+ * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+ * to get cas session resource if cas session resources is limited. If the client can't get the
+ * resource, this call returns {@link MediaCasException.InsufficientResourceException }.
+ *
* @return session the newly opened session.
*
* @throws IllegalStateException if the MediaCas instance is not valid.
@@ -930,6 +935,10 @@
* Open a session with usage and scrambling information, so that descrambler can be configured
* to descramble one or more streams scrambled by the conditional access system.
*
+ * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+ * to get cas session resource if cas session resources is limited. If the client can't get the
+ * resource, this call returns {@link MediaCasException.InsufficientResourceException}.
+ *
* @param sessionUsage used for the created session.
* @param scramblingMode used for the created session.
*
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 8e39dfa..fb95e98 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -466,10 +466,6 @@
return;
}
- List<RoutingSessionInfo> sessionInfos;
- synchronized (mSessionLock) {
- sessionInfos = new ArrayList<>(mSessionInfo.values());
- }
try {
mRemoteCallback.updateState(mProviderInfo);
} catch (RemoteException ex) {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 37e2ab5..bde45d7 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -58,7 +58,7 @@
// TODO: Add method names at the beginning of log messages. (e.g. updateControllerOnHandler)
// Not only MediaRouter2, but also to service / manager / provider.
// TODO: ensure thread-safe and document it
-public class MediaRouter2 {
+public final class MediaRouter2 {
private static final String TAG = "MR2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final Object sRouterLock = new Object();
@@ -93,9 +93,9 @@
MediaRouter2Stub mStub;
@GuardedBy("sRouterLock")
- private Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
+ private final Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
- private AtomicInteger mControllerCreationRequestCnt = new AtomicInteger(1);
+ private final AtomicInteger mControllerCreationRequestCnt = new AtomicInteger(1);
final Handler mHandler;
@GuardedBy("sRouterLock")
@@ -419,8 +419,7 @@
controller.release();
- final int requestId;
- requestId = mControllerCreationRequestCnt.getAndIncrement();
+ final int requestId = mControllerCreationRequestCnt.getAndIncrement();
ControllerCreationRequest request =
new ControllerCreationRequest(requestId, controller, route);
@@ -610,10 +609,16 @@
}
if (sessionInfo != null) {
- RoutingController newController = new RoutingController(sessionInfo);
- synchronized (sRouterLock) {
- mRoutingControllers.put(newController.getId(), newController);
+ RoutingController newController;
+ if (sessionInfo.isSystemSession()) {
+ newController = getSystemController();
+ } else {
+ newController = new RoutingController(sessionInfo);
+ synchronized (sRouterLock) {
+ mRoutingControllers.put(newController.getId(), newController);
+ }
}
+ //TODO: Determine oldController properly when transfer is launched by Output Switcher.
notifyTransferred(matchingRequest != null ? matchingRequest.mController :
getSystemController(), newController);
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 11c9fe1..88bcd6a 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -50,7 +50,7 @@
* A class that monitors and controls media routing of other apps.
* @hide
*/
-public class MediaRouter2Manager {
+public final class MediaRouter2Manager {
private static final String TAG = "MR2Manager";
private static final Object sLock = new Object();
@@ -61,7 +61,7 @@
final String mPackageName;
- private Context mContext;
+ private final Context mContext;
@GuardedBy("sLock")
private Client mClient;
private final IMediaRouterService mMediaRouterService;
@@ -74,7 +74,7 @@
@NonNull
final ConcurrentMap<String, List<String>> mPreferredFeaturesMap = new ConcurrentHashMap<>();
- private AtomicInteger mNextRequestId = new AtomicInteger(1);
+ private final AtomicInteger mNextRequestId = new AtomicInteger(1);
/**
* Gets an instance of media router manager that controls media route of other applications.
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 392e8fe..a5da648 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -20,9 +20,10 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.lang.ref.WeakReference;
/**
@@ -158,6 +159,7 @@
/**
* Indicates the state of the Visualizer instance
*/
+ @GuardedBy("mStateLock")
private int mState = STATE_UNINITIALIZED;
/**
* Lock to synchronize access to mState
@@ -166,6 +168,7 @@
/**
* System wide unique Identifier of the visualizer engine used by this Visualizer instance
*/
+ @GuardedBy("mStateLock")
@UnsupportedAppUsage
private int mId;
@@ -176,19 +179,24 @@
/**
* Handler for events coming from the native code
*/
- private NativeEventHandler mNativeEventHandler = null;
+ @GuardedBy("mListenerLock")
+ private Handler mNativeEventHandler = null;
/**
* PCM and FFT capture listener registered by client
*/
+ @GuardedBy("mListenerLock")
private OnDataCaptureListener mCaptureListener = null;
/**
* Server Died listener registered by client
*/
+ @GuardedBy("mListenerLock")
private OnServerDiedListener mServerDiedListener = null;
// accessed by native methods
- private long mNativeVisualizer;
- private long mJniData;
+ private long mNativeVisualizer; // guarded by a static lock in native code
+ private long mJniData; // set in native_setup, _release;
+ // get in native_release, _setEnabled, _setPeriodicCapture
+ // thus, effectively guarded by mStateLock
//--------------------------------------------------------------------------
// Constructor, Finalize
@@ -244,7 +252,9 @@
@Override
protected void finalize() {
- native_finalize();
+ synchronized (mStateLock) {
+ native_finalize();
+ }
}
/**
@@ -601,25 +611,28 @@
*/
public int setDataCaptureListener(OnDataCaptureListener listener,
int rate, boolean waveform, boolean fft) {
- synchronized (mListenerLock) {
- mCaptureListener = listener;
- }
if (listener == null) {
// make sure capture callback is stopped in native code
waveform = false;
fft = false;
}
- int status = native_setPeriodicCapture(rate, waveform, fft);
+ int status;
+ synchronized (mStateLock) {
+ status = native_setPeriodicCapture(rate, waveform, fft);
+ }
if (status == SUCCESS) {
- if ((listener != null) && (mNativeEventHandler == null)) {
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mNativeEventHandler = new NativeEventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mNativeEventHandler = new NativeEventHandler(this, looper);
- } else {
- mNativeEventHandler = null;
- status = ERROR_NO_INIT;
+ synchronized (mListenerLock) {
+ mCaptureListener = listener;
+ if ((listener != null) && (mNativeEventHandler == null)) {
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mNativeEventHandler = new Handler(looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mNativeEventHandler = new Handler(looper);
+ } else {
+ mNativeEventHandler = null;
+ status = ERROR_NO_INIT;
+ }
}
}
}
@@ -663,112 +676,61 @@
return SUCCESS;
}
- /**
- * Helper class to handle the forwarding of native events to the appropriate listeners
- */
- private class NativeEventHandler extends Handler
- {
- private Visualizer mVisualizer;
-
- public NativeEventHandler(Visualizer v, Looper looper) {
- super(looper);
- mVisualizer = v;
- }
-
- private void handleCaptureMessage(Message msg) {
- OnDataCaptureListener l = null;
- synchronized (mListenerLock) {
- l = mVisualizer.mCaptureListener;
- }
-
- if (l != null) {
- byte[] data = (byte[])msg.obj;
- int samplingRate = msg.arg1;
-
- switch(msg.what) {
- case NATIVE_EVENT_PCM_CAPTURE:
- l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
- break;
- case NATIVE_EVENT_FFT_CAPTURE:
- l.onFftDataCapture(mVisualizer, data, samplingRate);
- break;
- default:
- Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what);
- break;
- }
- }
- }
-
- private void handleServerDiedMessage(Message msg) {
- OnServerDiedListener l = null;
- synchronized (mListenerLock) {
- l = mVisualizer.mServerDiedListener;
- }
-
- if (l != null)
- l.onServerDied();
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (mVisualizer == null) {
- return;
- }
-
- switch(msg.what) {
- case NATIVE_EVENT_PCM_CAPTURE:
- case NATIVE_EVENT_FFT_CAPTURE:
- handleCaptureMessage(msg);
- break;
- case NATIVE_EVENT_SERVER_DIED:
- handleServerDiedMessage(msg);
- break;
- default:
- Log.e(TAG,"Unknown native event: "+msg.what);
- break;
- }
- }
- }
-
//---------------------------------------------------------
// Interface definitions
//--------------------
private static native final void native_init();
+ @GuardedBy("mStateLock")
private native final int native_setup(Object audioeffect_this,
int audioSession,
int[] id,
String opPackageName);
+ @GuardedBy("mStateLock")
private native final void native_finalize();
+ @GuardedBy("mStateLock")
private native final void native_release();
+ @GuardedBy("mStateLock")
private native final int native_setEnabled(boolean enabled);
+ @GuardedBy("mStateLock")
private native final boolean native_getEnabled();
+ @GuardedBy("mStateLock")
private native final int native_setCaptureSize(int size);
+ @GuardedBy("mStateLock")
private native final int native_getCaptureSize();
+ @GuardedBy("mStateLock")
private native final int native_setScalingMode(int mode);
+ @GuardedBy("mStateLock")
private native final int native_getScalingMode();
+ @GuardedBy("mStateLock")
private native final int native_setMeasurementMode(int mode);
+ @GuardedBy("mStateLock")
private native final int native_getMeasurementMode();
+ @GuardedBy("mStateLock")
private native final int native_getSamplingRate();
+ @GuardedBy("mStateLock")
private native final int native_getWaveForm(byte[] waveform);
+ @GuardedBy("mStateLock")
private native final int native_getFft(byte[] fft);
+ @GuardedBy("mStateLock")
private native final int native_getPeakRms(MeasurementPeakRms measurement);
+ @GuardedBy("mStateLock")
private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
//---------------------------------------------------------
@@ -776,17 +738,47 @@
//--------------------
@SuppressWarnings("unused")
private static void postEventFromNative(Object effect_ref,
- int what, int arg1, int arg2, Object obj) {
- Visualizer visu = (Visualizer)((WeakReference)effect_ref).get();
- if (visu == null) {
- return;
- }
+ int what, int samplingRate, byte[] data) {
+ final Visualizer visualizer = (Visualizer) ((WeakReference) effect_ref).get();
+ if (visualizer == null) return;
- if (visu.mNativeEventHandler != null) {
- Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
- visu.mNativeEventHandler.sendMessage(m);
+ final Handler handler;
+ synchronized (visualizer.mListenerLock) {
+ handler = visualizer.mNativeEventHandler;
}
+ if (handler == null) return;
+ switch (what) {
+ case NATIVE_EVENT_PCM_CAPTURE:
+ case NATIVE_EVENT_FFT_CAPTURE:
+ handler.post(() -> {
+ final OnDataCaptureListener l;
+ synchronized (visualizer.mListenerLock) {
+ l = visualizer.mCaptureListener;
+ }
+ if (l != null) {
+ if (what == NATIVE_EVENT_PCM_CAPTURE) {
+ l.onWaveFormDataCapture(visualizer, data, samplingRate);
+ } else { // what == NATIVE_EVENT_FFT_CAPTURE
+ l.onFftDataCapture(visualizer, data, samplingRate);
+ }
+ }
+ });
+ break;
+ case NATIVE_EVENT_SERVER_DIED:
+ handler.post(() -> {
+ final OnServerDiedListener l;
+ synchronized (visualizer.mListenerLock) {
+ l = visualizer.mServerDiedListener;
+ }
+ if (l != null) {
+ l.onServerDied();
+ }
+ });
+ break;
+ default:
+ Log.e(TAG, "Unknown native event in postEventFromNative: " + what);
+ break;
+ }
}
}
-
diff --git a/media/java/android/media/tv/OWNERS b/media/java/android/media/tv/OWNERS
index 64c0bb5..a891154 100644
--- a/media/java/android/media/tv/OWNERS
+++ b/media/java/android/media/tv/OWNERS
@@ -3,3 +3,7 @@
shubang@google.com
quxiangfang@google.com
+# For android remote service
+per-file ITvRemoteServiceInput.aidl = file:/media/lib/tvremote/OWNERS
+per-file ITvRemoteProvider.aidl = file:/media/lib/tvremote/OWNERS
+
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index 40add56..975604c 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.filter.Filter;
import java.lang.annotation.Retention;
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index ea06632..525ee4d 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -23,7 +23,7 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 08a33f1..d24d752 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.TvInputService;
-import android.media.tv.tuner.TunerConstants.Result;
import android.media.tv.tuner.dvr.DvrPlayback;
import android.media.tv.tuner.dvr.DvrRecorder;
import android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener;
@@ -44,12 +43,14 @@
import android.media.tv.tuner.frontend.OnTuneEventListener;
import android.media.tv.tuner.frontend.ScanCallback;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -68,6 +69,96 @@
*/
@SystemApi
public class Tuner implements AutoCloseable {
+ /**
+ * Invalid TS packet ID.
+ */
+ public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
+ /**
+ * Invalid stream ID.
+ */
+ public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
+ /**
+ * Invalid filter ID.
+ */
+ public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
+ /**
+ * Invalid AV Sync ID.
+ */
+ public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
+ /**
+ * Invalid timestamp.
+ *
+ * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
+ * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
+ * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
+ *
+ * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
+ * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
+ * @see Tuner#getAvSyncTime(int)
+ */
+ public static final long INVALID_TIMESTAMP = -1L;
+
+
+ /** @hide */
+ @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScanType {}
+ /**
+ * Scan type undefined.
+ */
+ public static final int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+ /**
+ * Scan type auto.
+ *
+ * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked}
+ */
+ public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+ /**
+ * Blind scan.
+ *
+ * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
+ * implementation specific range.
+ */
+ public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
+
+
+ /** @hide */
+ @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
+ RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Result {}
+
+ /**
+ * Operation succeeded.
+ */
+ public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
+ /**
+ * Operation failed because the corresponding resources are not available.
+ */
+ public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
+ /**
+ * Operation failed because the corresponding resources are not initialized.
+ */
+ public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
+ /**
+ * Operation failed because it's not in a valid state.
+ */
+ public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
+ /**
+ * Operation failed because there are invalid arguments.
+ */
+ public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
+ /**
+ * Memory allocation failed.
+ */
+ public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
+ /**
+ * Operation failed due to unknown errors.
+ */
+ public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
+
+
+
private static final String TAG = "MediaTvTuner";
private static final boolean DEBUG = false;
@@ -93,23 +184,27 @@
public static final int DVR_TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
static {
- System.loadLibrary("media_tv_tuner");
- nativeInit();
+ try {
+ System.loadLibrary("media_tv_tuner");
+ nativeInit();
+ } catch (UnsatisfiedLinkError e) {
+ Log.d(TAG, "tuner JNI library not found!");
+ }
}
private final Context mContext;
private final TunerResourceManager mTunerResourceManager;
private final int mClientId;
- private List<Integer> mFrontendIds;
private Frontend mFrontend;
private EventHandler mHandler;
@Nullable
private FrontendInfo mFrontendInfo;
+ private Integer mFrontendHandle;
+ private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
- private List<Integer> mLnbIds;
private Lnb mLnb;
- private Integer mLnbId;
+ private Integer mLnbHandle;
@Nullable
private OnTuneEventListener mOnTuneEventListener;
@Nullable
@@ -233,7 +328,7 @@
/**
* Native method to open frontend of the given ID.
*/
- private native Frontend nativeOpenFrontendById(int id);
+ private native Frontend nativeOpenFrontendByHandle(int handle);
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
private native int nativeScan(int settingsType, FrontendSettings settings, int scanType);
@@ -250,7 +345,7 @@
private native TimeFilter nativeOpenTimeFilter();
private native List<Integer> nativeGetLnbIds();
- private native Lnb nativeOpenLnbById(int id);
+ private native Lnb nativeOpenLnbByHandle(int handle);
private native Lnb nativeOpenLnbByName(String name);
private native Descrambler nativeOpenDescrambler();
@@ -359,6 +454,10 @@
/**
* Tunes the frontend to using the settings given.
*
+ * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+ * to get frontend resource. If the client can't get the resource, this call returns {@link
+ * Result#RESULT_UNAVAILABLE}.
+ *
* <p>
* This locks the frontend to a frequency by providing signal
* delivery information. If previous tuning isn't completed, this stop the previous tuning, and
@@ -378,7 +477,8 @@
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int tune(@NonNull FrontendSettings settings) {
- TunerUtils.checkTunerPermission(mContext);
+ mFrontendType = settings.getType();
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
mFrontendInfo = null;
return nativeTune(settings.getType(), settings);
}
@@ -411,14 +511,15 @@
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
- public int scan(@NonNull FrontendSettings settings, @TunerConstants.ScanType int scanType,
+ public int scan(@NonNull FrontendSettings settings, @ScanType int scanType,
@NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
- TunerUtils.checkTunerPermission(mContext);
if (mScanCallback != null || mScanCallbackExecutor != null) {
throw new IllegalStateException(
"Scan already in progress. stopScan must be called before a new scan can be "
+ "started.");
}
+ mFrontendType = settings.getType();
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
mScanCallback = scanCallback;
mScanCallbackExecutor = executor;
mFrontendInfo = null;
@@ -446,6 +547,16 @@
return retVal;
}
+ private boolean requestFrontend() {
+ int[] feId = new int[1];
+ TunerFrontendRequest request = new TunerFrontendRequest(mClientId, mFrontendType);
+ boolean granted = mTunerResourceManager.requestFrontend(request, feId);
+ if (granted) {
+ mFrontendHandle = feId[0];
+ }
+ return granted;
+ }
+
/**
* Sets Low-Noise Block downconverter (LNB) for satellite frontend.
*
@@ -498,7 +609,7 @@
public int getAvSyncHwId(@NonNull Filter filter) {
TunerUtils.checkTunerPermission(mContext);
Integer id = nativeGetAvSyncHwId(filter);
- return id == null ? TunerConstants.INVALID_AV_SYNC_ID : id;
+ return id == null ? INVALID_AV_SYNC_ID : id;
}
/**
@@ -514,7 +625,7 @@
public long getAvSyncTime(int avSyncHwId) {
TunerUtils.checkTunerPermission(mContext);
Long time = nativeGetAvSyncTime(avSyncHwId);
- return time == null ? TunerConstants.TIMESTAMP_UNAVAILABLE : time;
+ return time == null ? INVALID_TIMESTAMP : time;
}
/**
@@ -579,22 +690,6 @@
return nativeGetDemuxCapabilities();
}
- private List<Integer> getFrontendIds() {
- mFrontendIds = nativeGetFrontendIds();
- return mFrontendIds;
- }
-
- private Frontend openFrontendById(int id) {
- if (mFrontendIds == null) {
- mFrontendIds = getFrontendIds();
- }
- if (!mFrontendIds.contains(id)) {
- return null;
- }
- mFrontend = nativeOpenFrontendById(id);
- return mFrontend;
- }
-
private void onFrontendEvent(int eventType) {
if (mOnTunerEventExecutor != null && mOnTuneEventListener != null) {
mOnTunerEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType));
@@ -731,11 +826,9 @@
public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(cb, "LnbCallback must not be null");
- TunerUtils.checkTunerPermission(mContext);
- if (mLnbId == null && !requestLnb()) {
- return null;
- }
- return nativeOpenLnbById(mLnbId);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB);
+ // TODO: update JNI code for LNB handle,
+ return nativeOpenLnbByHandle(mLnbHandle);
}
/**
@@ -763,7 +856,7 @@
TunerLnbRequest request = new TunerLnbRequest(mClientId);
boolean granted = mTunerResourceManager.requestLnb(request, lnbId);
if (granted) {
- mLnbId = lnbId[0];
+ mLnbHandle = lnbId[0];
}
return granted;
}
@@ -778,22 +871,6 @@
return nativeOpenTimeFilter();
}
- private List<Integer> getLnbIds() {
- mLnbIds = nativeGetLnbIds();
- return mLnbIds;
- }
-
- private Lnb openLnbById(int id) {
- if (mLnbIds == null) {
- mLnbIds = getLnbIds();
- }
- if (!mLnbIds.contains(id)) {
- return null;
- }
- mLnb = nativeOpenLnbById(id);
- return mLnb;
- }
-
private void onLnbEvent(int eventType) {
if (mHandler != null) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LNB_EVENT, eventType, 0));
@@ -857,4 +934,23 @@
DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize);
return dvr;
}
+
+ private boolean checkResource(int resourceType) {
+ // TODO: demux and descrambler
+ switch (resourceType) {
+ case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
+ if (mFrontendHandle == null && !requestFrontend()) {
+ return false;
+ }
+ break;
+ }
+ case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: {
+ if (mLnbHandle == null && !requestLnb()) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+ }
}
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
deleted file mode 100644
index 6d89962..0000000
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.tuner;
-
-import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Constants for tuner framework.
- *
- * @hide
- */
-@SystemApi
-public final class TunerConstants {
- /**
- * Invalid TS packet ID.
- */
- public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
- /**
- * Invalid stream ID.
- */
- public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
- /**
- * Invalid filter ID.
- */
- public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
- /**
- * Invalid AV Sync ID.
- */
- public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
- /**
- * Timestamp is unavailable.
- *
- * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
- * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
- * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
- *
- * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
- * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
- * @see Tuner#getAvSyncTime(int)
- * @hide
- */
- public static final long TIMESTAMP_UNAVAILABLE = -1L;
-
- /** @hide */
- @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ScanType {}
- /**
- * Scan type undefined.
- */
- public static final int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
- /**
- * Scan type auto.
- *
- * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked}
- */
- public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
- /**
- * Blind scan.
- *
- * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
- * implementation specific range.
- */
- public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
-
- /** @hide */
- @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
- RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Result {}
-
- /**
- * Operation succeeded.
- */
- public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
- /**
- * Operation failed because the corresponding resources are not available.
- */
- public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
- /**
- * Operation failed because the corresponding resources are not initialized.
- */
- public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
- /**
- * Operation failed because it's not in a valid state.
- */
- public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
- /**
- * Operation failed because there are invalid arguments.
- */
- public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
- /**
- * Memory allocation failed.
- */
- public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
- /**
- * Operation failed due to unknown errors.
- */
- public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
-
- private TunerConstants() {
- }
-}
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 2258ee5..c3be12a 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -171,22 +171,22 @@
*/
@Nullable
public static void throwExceptionForResult(
- @TunerConstants.Result int r, @Nullable String msg) {
+ @Tuner.Result int r, @Nullable String msg) {
if (msg == null) {
msg = "";
}
switch (r) {
- case TunerConstants.RESULT_INVALID_ARGUMENT:
+ case Tuner.RESULT_INVALID_ARGUMENT:
throw new IllegalArgumentException(msg);
- case TunerConstants.RESULT_INVALID_STATE:
+ case Tuner.RESULT_INVALID_STATE:
throw new IllegalStateException(msg);
- case TunerConstants.RESULT_NOT_INITIALIZED:
+ case Tuner.RESULT_NOT_INITIALIZED:
throw new IllegalStateException("Invalid state: not initialized. " + msg);
- case TunerConstants.RESULT_OUT_OF_MEMORY:
+ case Tuner.RESULT_OUT_OF_MEMORY:
throw new OutOfMemoryError(msg);
- case TunerConstants.RESULT_UNAVAILABLE:
+ case Tuner.RESULT_UNAVAILABLE:
throw new IllegalStateException("Invalid state: resource unavailable. " + msg);
- case TunerConstants.RESULT_UNKNOWN_ERROR:
+ case Tuner.RESULT_UNKNOWN_ERROR:
throw new RuntimeException("Unknown error" + msg);
default:
break;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index dbd9db4..0d10d94 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -21,7 +21,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
@@ -69,7 +69,7 @@
*/
public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
- long mNativeContext;
+ private long mNativeContext;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index c1c6c62..dbda7bb 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -19,7 +19,7 @@
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
@@ -30,7 +30,7 @@
*/
@SystemApi
public class DvrRecorder implements AutoCloseable {
- long mNativeContext;
+ private long mNativeContext;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
index 8a29442..e40b080 100644
--- a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
@@ -125,8 +125,8 @@
* Builder for {@link AlpFilterConfiguration}.
*/
public static final class Builder {
- private int mPacketType;
- private int mLengthType;
+ private int mPacketType = PACKET_TYPE_IPV4;
+ private int mLengthType = LENGTH_TYPE_UNDEFINED;
private Settings mSettings;
private Builder() {
@@ -136,6 +136,7 @@
* Sets packet type.
*
* <p>The meaning of each packet type value is shown in ATSC A/330:2019 table 5.2.
+ * <p>Default value is {@link #PACKET_TYPE_IPV4}.
*/
@NonNull
public Builder setPacketType(int packetType) {
@@ -144,6 +145,8 @@
}
/**
* Sets length type.
+ *
+ * <p>Default value is {@link #LENGTH_TYPE_UNDEFINED}.
*/
@NonNull
public Builder setLengthType(@LengthType int lengthType) {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 4777fe8..8dc0622 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -22,7 +22,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index 04f3410..de75a4f 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -106,11 +106,11 @@
* Builder for {@link IpFilterConfiguration}.
*/
public static final class Builder {
- private byte[] mSrcIpAddress;
- private byte[] mDstIpAddress;
- private int mSrcPort;
- private int mDstPort;
- private boolean mPassthrough;
+ private byte[] mSrcIpAddress = {0, 0, 0, 0};
+ private byte[] mDstIpAddress = {0, 0, 0, 0};;
+ private int mSrcPort = 0;
+ private int mDstPort = 0;
+ private boolean mPassthrough = false;
private Settings mSettings;
private Builder() {
@@ -118,6 +118,8 @@
/**
* Sets source IP address.
+ *
+ * <p>Default value is 0.0.0.0, an invalid IP address.
*/
@NonNull
public Builder setSrcIpAddress(@NonNull byte[] srcIpAddress) {
@@ -126,6 +128,8 @@
}
/**
* Sets destination IP address.
+ *
+ * <p>Default value is 0.0.0.0, an invalid IP address.
*/
@NonNull
public Builder setDstIpAddress(@NonNull byte[] dstIpAddress) {
@@ -134,6 +138,8 @@
}
/**
* Sets source port.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setSrcPort(int srcPort) {
@@ -142,6 +148,8 @@
}
/**
* Sets destination port.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setDstPort(int dstPort) {
@@ -150,6 +158,8 @@
}
/**
* Sets passthrough.
+ *
+ * <p>Default value is {@code false}.
*/
@NonNull
public Builder setPassthrough(boolean passthrough) {
diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
index c0453b4..f19edc9 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
@@ -21,6 +21,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
+import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.TunerUtils;
/**
@@ -67,7 +68,7 @@
* Builder for {@link IpFilterConfiguration}.
*/
public static final class Builder {
- private int mMmtpPid;
+ private int mMmtpPid = Tuner.INVALID_TS_PID;
private Settings mSettings;
private Builder() {
@@ -75,6 +76,8 @@
/**
* Sets MMTP Packet ID.
+ *
+ * <p>Default value is {@link Tuner#INVALID_TS_PID}.
*/
@NonNull
public Builder setMmtpPacketId(int mmtpPid) {
diff --git a/media/java/android/media/tv/tuner/filter/TimeFilter.java b/media/java/android/media/tv/tuner/filter/TimeFilter.java
index 371ccc4..be0a055 100644
--- a/media/java/android/media/tv/tuner/filter/TimeFilter.java
+++ b/media/java/android/media/tv/tuner/filter/TimeFilter.java
@@ -17,8 +17,8 @@
package android.media.tv.tuner.filter;
import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
/**
@@ -35,17 +35,6 @@
@SystemApi
public class TimeFilter implements AutoCloseable {
- /**
- * Timestamp is unavailable.
- *
- * <p>Returned by {@link #getSourceTime()} or {@link #getTimeStamp()} when the requested
- * timestamp is not available.
- *
- * @see #getSourceTime()
- * @see #getTimeStamp()
- */
- public static final long TIMESTAMP_UNAVAILABLE = -1L;
-
private native int nativeSetTimestamp(long timestamp);
private native int nativeClearTimestamp();
@@ -108,12 +97,12 @@
*
* @return current timestamp in the time filter. It's based on the 90KHz counter, and it's
* the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019. The
- * timestamps may or may not be related to PTS or DTS. Returns {@link #TIMESTAMP_UNAVAILABLE}
- * if the timestamp is never set.
+ * timestamps may or may not be related to PTS or DTS. Returns
+ * {@link Tuner#INVALID_TIMESTAMP} if the timestamp is never set.
*/
public long getTimeStamp() {
if (!mEnable) {
- return TIMESTAMP_UNAVAILABLE;
+ return Tuner.INVALID_TIMESTAMP;
}
return nativeGetTimestamp();
}
@@ -126,11 +115,11 @@
* @return first timestamp of incoming data stream. It's based on the 90KHz counter, and
* it's the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019.
* The timestamps may or may not be related to PTS or DTS. Returns
- * {@link #TIMESTAMP_UNAVAILABLE} if the timestamp is not available.
+ * {@link Tuner#INVALID_TIMESTAMP} if the timestamp is not available.
*/
public long getSourceTime() {
if (!mEnable) {
- return TIMESTAMP_UNAVAILABLE;
+ return Tuner.INVALID_TIMESTAMP;
}
return nativeGetSourceTime();
}
@@ -144,7 +133,7 @@
@Override
public void close() {
int res = nativeClose();
- if (res != TunerConstants.RESULT_SUCCESS) {
+ if (res != Tuner.RESULT_SUCCESS) {
TunerUtils.throwExceptionForResult(res, "Failed to close time filter.");
}
}
diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
index c5191bf..eb1de52 100644
--- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
@@ -110,9 +110,9 @@
* Builder for {@link TlvFilterConfiguration}.
*/
public static final class Builder {
- private int mPacketType;
- private boolean mIsCompressedIpPacket;
- private boolean mPassthrough;
+ private int mPacketType = PACKET_TYPE_NULL;
+ private boolean mIsCompressedIpPacket = false;
+ private boolean mPassthrough = false;
private Settings mSettings;
private Builder() {
@@ -122,6 +122,7 @@
* Sets packet type.
*
* <p>The description of each packet type value is shown in ITU-R BT.1869 table 2.
+ * <p>Default value is {@link #PACKET_TYPE_NULL}.
*/
@NonNull
public Builder setPacketType(int packetType) {
@@ -130,6 +131,8 @@
}
/**
* Sets whether the data is compressed IP packet.
+ *
+ * <p>Default value is {@code false}.
*/
@NonNull
public Builder setCompressedIpPacket(boolean isCompressedIpPacket) {
@@ -138,6 +141,8 @@
}
/**
* Sets whether it's passthrough.
+ *
+ * <p>Default value is {@code false}.
*/
@NonNull
public Builder setPassthrough(boolean passthrough) {
diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
index a7140eb..0579269 100644
--- a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
@@ -65,7 +65,7 @@
* Builder for {@link TsFilterConfiguration}.
*/
public static final class Builder {
- private int mTpid;
+ private int mTpid = 0;
private Settings mSettings;
private Builder() {
@@ -74,6 +74,8 @@
/**
* Sets Tag Protocol ID.
*
+ * <p>Default value is 0.
+ *
* @param tpid the Tag Protocol ID.
*/
@NonNull
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index 7b85fa8..e68585d 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -213,14 +214,29 @@
/**
* Builder for {@link AnalogFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mSignalType;
- private int mSifStandard;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mSignalType = SIGNAL_TYPE_UNDEFINED;
+ private int mSifStandard = SIF_UNDEFINED;
private Builder() {}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets analog signal type.
+ *
+ * <p>Default value is {@link #SIGNAL_TYPE_UNDEFINED}.
*/
@NonNull
public Builder setSignalType(@SignalType int signalType) {
@@ -230,6 +246,8 @@
/**
* Sets Standard Interchange Format (SIF).
+ *
+ * <p>Default value is {@link #SIF_UNDEFINED}.
*/
@NonNull
public Builder setSifStandard(@SifStandard int sifStandard) {
@@ -244,10 +262,5 @@
public AnalogFrontendSettings build() {
return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard);
}
-
- @Override
- Builder self() {
- return this;
- }
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index b40ab00..bf4f3b2 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -325,16 +326,31 @@
/**
* Builder for {@link Atsc3FrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mBandwidth;
- private int mDemodOutputFormat;
- private Atsc3PlpSettings[] mPlpSettings;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mBandwidth = BANDWIDTH_UNDEFINED;
+ private int mDemodOutputFormat = DEMOD_OUTPUT_FORMAT_UNDEFINED;
+ private Atsc3PlpSettings[] mPlpSettings = {};
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets bandwidth.
+ *
+ * <p>Default value is {@link #BANDWIDTH_UNDEFINED}.
*/
@NonNull
public Builder setBandwidth(int bandwidth) {
@@ -343,6 +359,8 @@
}
/**
* Sets Demod Output Format.
+ *
+ * <p>Default value is {@link #DEMOD_OUTPUT_FORMAT_UNDEFINED}.
*/
@NonNull
public Builder setDemodOutputFormat(@DemodOutputFormat int demodOutputFormat) {
@@ -351,6 +369,8 @@
}
/**
* Sets PLP Settings.
+ *
+ * <p>Default value an empty array.
*/
@NonNull
public Builder setPlpSettings(@NonNull Atsc3PlpSettings[] plpSettings) {
@@ -366,11 +386,6 @@
return new Atsc3FrontendSettings(
mFrequency, mBandwidth, mDemodOutputFormat, mPlpSettings);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
index fc82a1c..0674f6e 100644
--- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -91,14 +92,29 @@
/**
* Builder for {@link AtscFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mModulation;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mModulation = MODULATION_UNDEFINED;
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Modulation.
+ *
+ * <p>Default value is {@link #MODULATION_UNDEFINED}.
*/
@NonNull
public Builder setModulation(@Modulation int modulation) {
@@ -113,11 +129,6 @@
public AtscFrontendSettings build() {
return new AtscFrontendSettings(mFrequency, mModulation);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index 705d520..121de5d 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -144,17 +145,17 @@
private final int mModulation;
- private final long mFec;
+ private final long mInnerFec;
private final int mSymbolRate;
private final int mOuterFec;
private final int mAnnex;
private final int mSpectralInversion;
- private DvbcFrontendSettings(int frequency, int modulation, long fec, int symbolRate,
+ private DvbcFrontendSettings(int frequency, int modulation, long innerFec, int symbolRate,
int outerFec, int annex, int spectralInversion) {
super(frequency);
mModulation = modulation;
- mFec = fec;
+ mInnerFec = innerFec;
mSymbolRate = symbolRate;
mOuterFec = outerFec;
mAnnex = annex;
@@ -172,8 +173,8 @@
* Gets Inner Forward Error Correction.
*/
@InnerFec
- public long getFec() {
- return mFec;
+ public long getInnerFec() {
+ return mInnerFec;
}
/**
* Gets Symbol Rate in symbols per second.
@@ -218,19 +219,34 @@
/**
* Builder for {@link DvbcFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mModulation;
- private long mFec;
- private int mSymbolRate;
- private int mOuterFec;
- private int mAnnex;
- private int mSpectralInversion;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mModulation = MODULATION_UNDEFINED;
+ private long mInnerFec = FEC_UNDEFINED;
+ private int mSymbolRate = 0;
+ private int mOuterFec = OUTER_FEC_UNDEFINED;
+ private int mAnnex = ANNEX_UNDEFINED;
+ private int mSpectralInversion = SPECTRAL_INVERSION_UNDEFINED;
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Modulation.
+ *
+ * <p>Default value is {@link #MODULATION_UNDEFINED}.
*/
@NonNull
public Builder setModulation(@Modulation int modulation) {
@@ -239,14 +255,18 @@
}
/**
* Sets Inner Forward Error Correction.
+ *
+ * <p>Default value is {@link #FEC_UNDEFINED}.
*/
@NonNull
- public Builder setFec(@InnerFec long fec) {
- mFec = fec;
+ public Builder setInnerFec(@InnerFec long fec) {
+ mInnerFec = fec;
return this;
}
/**
* Sets Symbol Rate in symbols per second.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setSymbolRate(int symbolRate) {
@@ -255,6 +275,8 @@
}
/**
* Sets Outer Forward Error Correction.
+ *
+ * <p>Default value is {@link #OUTER_FEC_UNDEFINED}.
*/
@NonNull
public Builder setOuterFec(@OuterFec int outerFec) {
@@ -263,6 +285,8 @@
}
/**
* Sets Annex.
+ *
+ * <p>Default value is {@link #ANNEX_UNDEFINED}.
*/
@NonNull
public Builder setAnnex(@Annex int annex) {
@@ -271,6 +295,8 @@
}
/**
* Sets Spectral Inversion.
+ *
+ * <p>Default value is {@link #SPECTRAL_INVERSION_UNDEFINED}.
*/
@NonNull
public Builder setSpectralInversion(@SpectralInversion int spectralInversion) {
@@ -283,13 +309,8 @@
*/
@NonNull
public DvbcFrontendSettings build() {
- return new DvbcFrontendSettings(mFrequency, mModulation, mFec, mSymbolRate, mOuterFec,
- mAnnex, mSpectralInversion);
- }
-
- @Override
- Builder self() {
- return this;
+ return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate,
+ mOuterFec, mAnnex, mSpectralInversion);
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 4a4fed5..afc79ab 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -17,12 +17,14 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
@@ -303,21 +305,36 @@
/**
* Builder for {@link DvbsFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mModulation;
- private DvbsCodeRate mCodeRate;
- private int mSymbolRate;
- private int mRolloff;
- private int mPilot;
- private int mInputStreamId;
- private int mStandard;
- private int mVcmMode;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mModulation = MODULATION_UNDEFINED;
+ private DvbsCodeRate mCodeRate = null;
+ private int mSymbolRate = 0;
+ private int mRolloff = ROLLOFF_UNDEFINED;
+ private int mPilot = PILOT_UNDEFINED;
+ private int mInputStreamId = Tuner.INVALID_STREAM_ID;
+ private int mStandard = STANDARD_AUTO;
+ private int mVcmMode = VCM_MODE_UNDEFINED;
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Modulation.
+ *
+ * <p>Default value is {@link #MODULATION_UNDEFINED}.
*/
@NonNull
public Builder setModulation(@Modulation int modulation) {
@@ -326,6 +343,8 @@
}
/**
* Sets Code rate.
+ *
+ * <p>Default value is {@code null}.
*/
@NonNull
public Builder setCodeRate(@Nullable DvbsCodeRate codeRate) {
@@ -334,6 +353,8 @@
}
/**
* Sets Symbol Rate.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setSymbolRate(int symbolRate) {
@@ -342,6 +363,8 @@
}
/**
* Sets Rolloff.
+ *
+ * <p>Default value is {@link #ROLLOFF_UNDEFINED}.
*/
@NonNull
public Builder setRolloff(@Rolloff int rolloff) {
@@ -350,6 +373,8 @@
}
/**
* Sets Pilot mode.
+ *
+ * <p>Default value is {@link #PILOT_UNDEFINED}.
*/
@NonNull
public Builder setPilot(@Pilot int pilot) {
@@ -358,6 +383,8 @@
}
/**
* Sets Input Stream ID.
+ *
+ * <p>Default value is {@link Tuner#INVALID_STREAM_ID}.
*/
@NonNull
public Builder setInputStreamId(int inputStreamId) {
@@ -366,6 +393,8 @@
}
/**
* Sets Standard.
+ *
+ * <p>Default value is {@link #STANDARD_AUTO}.
*/
@NonNull
public Builder setStandard(@Standard int standard) {
@@ -374,6 +403,8 @@
}
/**
* Sets VCM mode.
+ *
+ * <p>Default value is {@link #VCM_MODE_UNDEFINED}.
*/
@NonNull
public Builder setVcmMode(@VcmMode int vcm) {
@@ -389,11 +420,6 @@
return new DvbsFrontendSettings(mFrequency, mModulation, mCodeRate, mSymbolRate,
mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index e99fd36f..67a9fdc 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -125,9 +126,9 @@
/** @hide */
@IntDef(flag = true,
prefix = "CONSTELLATION_",
- value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_CONSTELLATION_QPSK,
- CONSTELLATION_CONSTELLATION_16QAM, CONSTELLATION_CONSTELLATION_64QAM,
- CONSTELLATION_CONSTELLATION_256QAM})
+ value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK,
+ CONSTELLATION_16QAM, CONSTELLATION_64QAM,
+ CONSTELLATION_256QAM})
@Retention(RetentionPolicy.SOURCE)
public @interface Constellation {}
@@ -142,22 +143,22 @@
/**
* QPSK Constellation.
*/
- public static final int CONSTELLATION_CONSTELLATION_QPSK =
+ public static final int CONSTELLATION_QPSK =
Constants.FrontendDvbtConstellation.CONSTELLATION_QPSK;
/**
* 16QAM Constellation.
*/
- public static final int CONSTELLATION_CONSTELLATION_16QAM =
+ public static final int CONSTELLATION_16QAM =
Constants.FrontendDvbtConstellation.CONSTELLATION_16QAM;
/**
* 64QAM Constellation.
*/
- public static final int CONSTELLATION_CONSTELLATION_64QAM =
+ public static final int CONSTELLATION_64QAM =
Constants.FrontendDvbtConstellation.CONSTELLATION_64QAM;
/**
* 256QAM Constellation.
*/
- public static final int CONSTELLATION_CONSTELLATION_256QAM =
+ public static final int CONSTELLATION_256QAM =
Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM;
@@ -275,11 +276,11 @@
@IntDef(flag = true,
prefix = "GUARD_INTERVAL_",
value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO,
- GUARD_INTERVAL_INTERVAL_1_32, GUARD_INTERVAL_INTERVAL_1_16,
- GUARD_INTERVAL_INTERVAL_1_8, GUARD_INTERVAL_INTERVAL_1_4,
- GUARD_INTERVAL_INTERVAL_1_128,
- GUARD_INTERVAL_INTERVAL_19_128,
- GUARD_INTERVAL_INTERVAL_19_256})
+ GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16,
+ GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4,
+ GUARD_INTERVAL_1_128,
+ GUARD_INTERVAL_19_128,
+ GUARD_INTERVAL_19_256})
@Retention(RetentionPolicy.SOURCE)
public @interface GuardInterval {}
@@ -295,37 +296,37 @@
/**
* 1/32 Guard Interval.
*/
- public static final int GUARD_INTERVAL_INTERVAL_1_32 =
+ public static final int GUARD_INTERVAL_1_32 =
Constants.FrontendDvbtGuardInterval.INTERVAL_1_32;
/**
* 1/16 Guard Interval.
*/
- public static final int GUARD_INTERVAL_INTERVAL_1_16 =
+ public static final int GUARD_INTERVAL_1_16 =
Constants.FrontendDvbtGuardInterval.INTERVAL_1_16;
/**
* 1/8 Guard Interval.
*/
- public static final int GUARD_INTERVAL_INTERVAL_1_8 =
+ public static final int GUARD_INTERVAL_1_8 =
Constants.FrontendDvbtGuardInterval.INTERVAL_1_8;
/**
* 1/4 Guard Interval.
*/
- public static final int GUARD_INTERVAL_INTERVAL_1_4 =
+ public static final int GUARD_INTERVAL_1_4 =
Constants.FrontendDvbtGuardInterval.INTERVAL_1_4;
/**
* 1/128 Guard Interval.
*/
- public static final int GUARD_INTERVAL_INTERVAL_1_128 =
+ public static final int GUARD_INTERVAL_1_128 =
Constants.FrontendDvbtGuardInterval.INTERVAL_1_128;
/**
* 19/128 Guard Interval.
*/
- public static final int GUARD_INTERVAL_INTERVAL_19_128 =
+ public static final int GUARD_INTERVAL_19_128 =
Constants.FrontendDvbtGuardInterval.INTERVAL_19_128;
/**
* 19/256 Guard Interval.
*/
- public static final int GUARD_INTERVAL_INTERVAL_19_256 =
+ public static final int GUARD_INTERVAL_19_256 =
Constants.FrontendDvbtGuardInterval.INTERVAL_19_256;
/** @hide */
@@ -435,14 +436,14 @@
* Gets Code Rate for High Priority level.
*/
@CodeRate
- public int getHpCodeRate() {
+ public int getHighPriorityCodeRate() {
return mHpCodeRate;
}
/**
* Gets Code Rate for Low Priority level.
*/
@CodeRate
- public int getLpCodeRate() {
+ public int getLowPriorityCodeRate() {
return mLpCodeRate;
}
/**
@@ -506,26 +507,41 @@
/**
* Builder for {@link DvbtFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mTransmissionMode;
- private int mBandwidth;
- private int mConstellation;
- private int mHierarchy;
- private int mHpCodeRate;
- private int mLpCodeRate;
- private int mGuardInterval;
- private boolean mIsHighPriority;
- private int mStandard;
- private boolean mIsMiso;
- private int mPlpMode;
- private int mPlpId;
- private int mPlpGroupId;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mTransmissionMode = TRANSMISSION_MODE_UNDEFINED;
+ private int mBandwidth = BANDWIDTH_UNDEFINED;
+ private int mConstellation = CONSTELLATION_UNDEFINED;
+ private int mHierarchy = HIERARCHY_UNDEFINED;
+ private int mHpCodeRate = CODERATE_UNDEFINED;
+ private int mLpCodeRate = CODERATE_UNDEFINED;
+ private int mGuardInterval = GUARD_INTERVAL_UNDEFINED;
+ private boolean mIsHighPriority = false;
+ private int mStandard = STANDARD_AUTO;
+ private boolean mIsMiso = false;
+ private int mPlpMode = PLP_MODE_UNDEFINED;
+ private int mPlpId = 0;
+ private int mPlpGroupId = 0;
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Transmission Mode.
+ *
+ * <p>Default value is {@link #TRANSMISSION_MODE_UNDEFINED}.
*/
@NonNull
public Builder setTransmissionMode(@TransmissionMode int transmissionMode) {
@@ -534,6 +550,8 @@
}
/**
* Sets Bandwidth.
+ *
+ * <p>Default value is {@link #BANDWIDTH_UNDEFINED}.
*/
@NonNull
public Builder setBandwidth(@Bandwidth int bandwidth) {
@@ -542,6 +560,8 @@
}
/**
* Sets Constellation.
+ *
+ * <p>Default value is {@link #CONSTELLATION_UNDEFINED}.
*/
@NonNull
public Builder setConstellation(@Constellation int constellation) {
@@ -550,6 +570,8 @@
}
/**
* Sets Hierarchy.
+ *
+ * <p>Default value is {@link #HIERARCHY_UNDEFINED}.
*/
@NonNull
public Builder setHierarchy(@Hierarchy int hierarchy) {
@@ -558,22 +580,28 @@
}
/**
* Sets Code Rate for High Priority level.
+ *
+ * <p>Default value is {@link #CODERATE_UNDEFINED}.
*/
@NonNull
- public Builder setHpCodeRate(@CodeRate int hpCodeRate) {
+ public Builder setHighPriorityCodeRate(@CodeRate int hpCodeRate) {
mHpCodeRate = hpCodeRate;
return this;
}
/**
* Sets Code Rate for Low Priority level.
+ *
+ * <p>Default value is {@link #CODERATE_UNDEFINED}.
*/
@NonNull
- public Builder setLpCodeRate(@CodeRate int lpCodeRate) {
+ public Builder setLowPriorityCodeRate(@CodeRate int lpCodeRate) {
mLpCodeRate = lpCodeRate;
return this;
}
/**
* Sets Guard Interval.
+ *
+ * <p>Default value is {@link #GUARD_INTERVAL_UNDEFINED}.
*/
@NonNull
public Builder setGuardInterval(@GuardInterval int guardInterval) {
@@ -582,6 +610,8 @@
}
/**
* Sets whether it's high priority.
+ *
+ * <p>Default value is {@code false}.
*/
@NonNull
public Builder setHighPriority(boolean isHighPriority) {
@@ -590,6 +620,8 @@
}
/**
* Sets Standard.
+ *
+ * <p>Default value is {@link #STANDARD_AUTO}.
*/
@NonNull
public Builder setStandard(@Standard int standard) {
@@ -598,6 +630,8 @@
}
/**
* Sets whether it's MISO.
+ *
+ * <p>Default value is {@code false}.
*/
@NonNull
public Builder setMiso(boolean isMiso) {
@@ -606,6 +640,8 @@
}
/**
* Sets Physical Layer Pipe (PLP) Mode.
+ *
+ * <p>Default value is {@link #PLP_MODE_UNDEFINED}.
*/
@NonNull
public Builder setPlpMode(@PlpMode int plpMode) {
@@ -614,6 +650,8 @@
}
/**
* Sets Physical Layer Pipe (PLP) ID.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setPlpId(int plpId) {
@@ -622,6 +660,8 @@
}
/**
* Sets Physical Layer Pipe (PLP) group ID.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setPlpGroupId(int plpGroupId) {
@@ -638,11 +678,6 @@
mConstellation, mHierarchy, mHpCodeRate, mLpCodeRate, mGuardInterval,
mIsHighPriority, mStandard, mIsMiso, mPlpMode, mPlpId, mPlpGroupId);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 9071526..2f2fa97 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -17,9 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
-import android.annotation.IntRange;
import android.annotation.LongDef;
-import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
@@ -265,27 +263,4 @@
public int getFrequency() {
return mFrequency;
}
-
- /**
- * Builder for {@link FrontendSettings}.
- *
- * @param <T> The subclass to be built.
- */
- public abstract static class Builder<T extends Builder<T>> {
- /* package */ int mFrequency;
-
- /* package */ Builder() {}
-
- /**
- * Sets frequency in Hz.
- */
- @NonNull
- @IntRange(from = 1)
- public T setFrequency(int frequency) {
- mFrequency = frequency;
- return self();
- }
-
- /* package */ abstract T self();
- }
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 63de0334..ea91ee9 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -304,7 +304,7 @@
* and ETSI EN 302 307-2 V1.1.1.
*/
@FrontendSettings.InnerFec
- public long getFec() {
+ public long getInnerFec() {
if (mInnerFec == null) {
throw new IllegalStateException();
}
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 9b0e533..e0077ca 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -17,11 +17,13 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
@@ -224,19 +226,34 @@
/**
* Builder for {@link Isdbs3FrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mStreamId;
- private int mStreamIdType;
- private int mModulation;
- private int mCodeRate;
- private int mSymbolRate;
- private int mRolloff;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mStreamId = Tuner.INVALID_STREAM_ID;
+ private int mStreamIdType = IsdbsFrontendSettings.STREAM_ID_TYPE_ID;
+ private int mModulation = MODULATION_UNDEFINED;
+ private int mCodeRate = CODERATE_UNDEFINED;
+ private int mSymbolRate = 0;
+ private int mRolloff = ROLLOFF_UNDEFINED;
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Stream ID.
+ *
+ * <p>Default value is {@link Tuner#INVALID_STREAM_ID}.
*/
@NonNull
public Builder setStreamId(int streamId) {
@@ -245,6 +262,8 @@
}
/**
* Sets StreamIdType.
+ *
+ * <p>Default value is {@link IsdbsFrontendSettings#STREAM_ID_TYPE_ID}.
*/
@NonNull
public Builder setStreamIdType(@IsdbsFrontendSettings.StreamIdType int streamIdType) {
@@ -253,6 +272,8 @@
}
/**
* Sets Modulation.
+ *
+ * <p>Default value is {@link #MODULATION_UNDEFINED}.
*/
@NonNull
public Builder setModulation(@Modulation int modulation) {
@@ -261,6 +282,8 @@
}
/**
* Sets Code rate.
+ *
+ * <p>Default value is {@link #CODERATE_UNDEFINED}.
*/
@NonNull
public Builder setCodeRate(@CodeRate int codeRate) {
@@ -269,6 +292,8 @@
}
/**
* Sets Symbol Rate in symbols per second.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setSymbolRate(int symbolRate) {
@@ -277,6 +302,8 @@
}
/**
* Sets Roll off type.
+ *
+ * <p>Default value is {@link #ROLLOFF_UNDEFINED}.
*/
@NonNull
public Builder setRolloff(@Rolloff int rolloff) {
@@ -292,11 +319,6 @@
return new Isdbs3FrontendSettings(mFrequency, mStreamId, mStreamIdType, mModulation,
mCodeRate, mSymbolRate, mRolloff);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index 14c08b1..8dc591b 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -17,11 +17,13 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
@@ -208,19 +210,34 @@
/**
* Builder for {@link IsdbsFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mStreamId;
- private int mStreamIdType;
- private int mModulation;
- private int mCodeRate;
- private int mSymbolRate;
- private int mRolloff;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mStreamId = Tuner.INVALID_STREAM_ID;
+ private int mStreamIdType = STREAM_ID_TYPE_ID;
+ private int mModulation = MODULATION_UNDEFINED;
+ private int mCodeRate = CODERATE_UNDEFINED;
+ private int mSymbolRate = 0;
+ private int mRolloff = ROLLOFF_UNDEFINED;
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Stream ID.
+ *
+ * <p>Default value is {@link Tuner#INVALID_STREAM_ID}.
*/
@NonNull
public Builder setStreamId(int streamId) {
@@ -229,6 +246,8 @@
}
/**
* Sets StreamIdType.
+ *
+ * <p>Default value is {@link #STREAM_ID_TYPE_ID}.
*/
@NonNull
public Builder setStreamIdType(@StreamIdType int streamIdType) {
@@ -237,6 +256,8 @@
}
/**
* Sets Modulation.
+ *
+ * <p>Default value is {@link #MODULATION_UNDEFINED}.
*/
@NonNull
public Builder setModulation(@Modulation int modulation) {
@@ -245,6 +266,8 @@
}
/**
* Sets Code rate.
+ *
+ * <p>Default value is {@link #CODERATE_UNDEFINED}.
*/
@NonNull
public Builder setCodeRate(@CodeRate int codeRate) {
@@ -253,6 +276,8 @@
}
/**
* Sets Symbol Rate in symbols per second.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setSymbolRate(int symbolRate) {
@@ -261,6 +286,8 @@
}
/**
* Sets Roll off type.
+ *
+ * <p>Default value is {@link #ROLLOFF_UNDEFINED}.
*/
@NonNull
public Builder setRolloff(@Rolloff int rolloff) {
@@ -276,11 +303,6 @@
return new IsdbsFrontendSettings(mFrequency, mStreamId, mStreamIdType, mModulation,
mCodeRate, mSymbolRate, mRolloff);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index de3c80d..915380e 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -202,19 +203,34 @@
/**
* Builder for {@link IsdbtFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
- private int mModulation;
- private int mBandwidth;
- private int mMode;
- private int mCodeRate;
- private int mGuardInterval;
- private int mServiceAreaId;
+ public static class Builder {
+ private int mFrequency = 0;
+ private int mModulation = MODULATION_UNDEFINED;
+ private int mBandwidth = BANDWIDTH_UNDEFINED;
+ private int mMode = MODE_UNDEFINED;
+ private int mCodeRate = DvbtFrontendSettings.CODERATE_UNDEFINED;
+ private int mGuardInterval = DvbtFrontendSettings.GUARD_INTERVAL_UNDEFINED;
+ private int mServiceAreaId = 0;
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Modulation.
+ *
+ * <p>Default value is {@link #MODULATION_UNDEFINED}.
*/
@NonNull
public Builder setModulation(@Modulation int modulation) {
@@ -223,6 +239,8 @@
}
/**
* Sets Bandwidth.
+ *
+ * <p>Default value is {@link #BANDWIDTH_UNDEFINED}.
*/
@NonNull
public Builder setBandwidth(@Bandwidth int bandwidth) {
@@ -231,6 +249,8 @@
}
/**
* Sets ISDBT mode.
+ *
+ * <p>Default value is {@link #MODE_UNDEFINED}.
*/
@NonNull
public Builder setMode(@Mode int mode) {
@@ -239,14 +259,18 @@
}
/**
* Sets Code rate.
+ *
+ * <p>Default value is {@link DvbtFrontendSettings#CODERATE_UNDEFINED}.
*/
@NonNull
- public Builder setCodeRate(@CodeRate int codeRate) {
+ public Builder setCodeRate(@DvbtFrontendSettings.CodeRate int codeRate) {
mCodeRate = codeRate;
return this;
}
/**
* Sets Guard Interval.
+ *
+ * <p>Default value is {@link DvbtFrontendSettings#GUARD_INTERVAL_UNDEFINED}.
*/
@NonNull
public Builder setGuardInterval(@DvbtFrontendSettings.GuardInterval int guardInterval) {
@@ -255,6 +279,8 @@
}
/**
* Sets Service Area ID.
+ *
+ * <p>Default value is 0.
*/
@NonNull
public Builder setServiceAreaId(int serviceAreaId) {
@@ -270,11 +296,6 @@
return new IsdbtFrontendSettings(mFrequency, mModulation, mBandwidth, mMode, mCodeRate,
mGuardInterval, mServiceAreaId);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 20efaa1..77cac6e 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -19,6 +19,8 @@
import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
@@ -148,6 +150,53 @@
void shareFrontend(in int selfClientId, in int targetClientId);
/*
+ * This API is used by the Tuner framework to request an available demux from the TunerHAL.
+ *
+ * <p>There are three possible scenarios:
+ * <ul>
+ * <li>If there is demux available, the API would send the handle back.
+ *
+ * <li>If no Demux is available but the current request info can show higher priority than
+ * other uses of demuxes, the API will send
+ * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
+ * handle the resource reclaim on the holder of lower priority and notify the holder of its
+ * resource loss.
+ *
+ * <li>If no demux can be granted, the API would return false.
+ * <ul>
+ *
+ * @param request {@link TunerDemuxRequest} information of the current request.
+ * @param demuxHandle a one-element array to return the granted demux handle.
+ *
+ * @return true if there is demux granted.
+ */
+ boolean requestDemux(in TunerDemuxRequest request, out int[] demuxHandle);
+
+ /*
+ * This API is used by the Tuner framework to request an available descrambler from the
+ * TunerHAL.
+ *
+ * <p>There are three possible scenarios:
+ * <ul>
+ * <li>If there is descrambler available, the API would send the handle back.
+ *
+ * <li>If no Descrambler is available but the current request info can show higher priority than
+ * other uses of Descrambler, the API will send
+ * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
+ * handle the resource reclaim on the holder of lower priority and notify the holder of its
+ * resource loss.
+ *
+ * <li>If no Descrambler can be granted, the API would return false.
+ * <ul>
+ *
+ * @param request {@link TunerDescramblerRequest} information of the current request.
+ * @param descramblerHandle a one-element array to return the granted descrambler handle.
+ *
+ * @return true if there is Descrambler granted.
+ */
+ boolean requestDescrambler(in TunerDescramblerRequest request, out int[] descramblerHandle);
+
+ /*
* This API is used by the Tuner framework to request an available Cas session. This session
* needs to be under the CAS system with the id indicated in the {@code request}.
*
@@ -210,6 +259,24 @@
void releaseFrontend(in int frontendId);
/*
+ * Notifies the TRM that the Demux with the given handle was released.
+ *
+ * <p>Client must call this whenever it releases a demux.
+ *
+ * @param demuxHandle the handle of the released Tuner Demux.
+ */
+ void releaseDemux(in int demuxHandle);
+
+ /*
+ * Notifies the TRM that the Descrambler with the given handle was released.
+ *
+ * <p>Client must call this whenever it releases a descrambler.
+ *
+ * @param demuxHandle the handle of the released Tuner Descrambler.
+ */
+ void releaseDescrambler(in int descramblerHandle);
+
+ /*
* Notifies the TRM that the given Cas session has been released.
*
* <p>Client must call this whenever it releases a Cas session.
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
new file mode 100644
index 0000000..919a215
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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 android.media.tv.tunerresourcemanager;
+
+/**
+ * Information required to request a Tuner Demux.
+ *
+ * @hide
+ */
+parcelable TunerDemuxRequest;
\ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java
new file mode 100644
index 0000000..34a7761
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 android.media.tv.tunerresourcemanager;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Information required to request a Tuner Demux.
+ *
+ * @hide
+ */
+public final class TunerDemuxRequest implements Parcelable {
+ static final String TAG = "TunerDemuxRequest";
+
+ public static final
+ @NonNull
+ Parcelable.Creator<TunerDemuxRequest> CREATOR =
+ new Parcelable.Creator<TunerDemuxRequest>() {
+ @Override
+ public TunerDemuxRequest createFromParcel(Parcel source) {
+ try {
+ return new TunerDemuxRequest(source);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TunerDemuxRequest from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TunerDemuxRequest[] newArray(int size) {
+ return new TunerDemuxRequest[size];
+ }
+ };
+
+ /**
+ * Client id of the client that sends the request.
+ */
+ private final int mClientId;
+
+ private TunerDemuxRequest(@NonNull Parcel source) {
+ mClientId = source.readInt();
+ }
+
+ /**
+ * Constructs a new {@link TunerDemuxRequest} with the given parameters.
+ *
+ * @param clientId id of the client.
+ */
+ public TunerDemuxRequest(int clientId) {
+ mClientId = clientId;
+ }
+
+ /**
+ * Returns the id of the client.
+ */
+ public int getClientId() {
+ return mClientId;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder(128);
+ b.append("TunerDemuxRequest {clientId=").append(mClientId);
+ b.append("}");
+ return b.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mClientId);
+ }
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl
new file mode 100644
index 0000000..fbafb3b
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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 android.media.tv.tunerresourcemanager;
+
+/**
+ * Information required to request a Tuner Descrambler.
+ *
+ * @hide
+ */
+parcelable TunerDescramblerRequest;
\ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java
new file mode 100644
index 0000000..5816287
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 android.media.tv.tunerresourcemanager;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Information required to request a Tuner Descrambler.
+ *
+ * @hide
+ */
+public final class TunerDescramblerRequest implements Parcelable {
+ static final String TAG = "TunerDescramblerRequest";
+
+ public static final
+ @NonNull
+ Parcelable.Creator<TunerDescramblerRequest> CREATOR =
+ new Parcelable.Creator<TunerDescramblerRequest>() {
+ @Override
+ public TunerDescramblerRequest createFromParcel(Parcel source) {
+ try {
+ return new TunerDescramblerRequest(source);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TunerDescramblerRequest from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TunerDescramblerRequest[] newArray(int size) {
+ return new TunerDescramblerRequest[size];
+ }
+ };
+
+ /**
+ * Client id of the client that sends the request.
+ */
+ private final int mClientId;
+
+ private TunerDescramblerRequest(@NonNull Parcel source) {
+ mClientId = source.readInt();
+ }
+
+ /**
+ * Constructs a new {@link TunerDescramblerRequest} with the given parameters.
+ *
+ * @param clientId id of the client.
+ */
+ public TunerDescramblerRequest(int clientId) {
+ mClientId = clientId;
+ }
+
+ /**
+ * Returns the id of the client.
+ */
+ public int getClientId() {
+ return mClientId;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder(128);
+ b.append("TunerDescramblerRequest {clientId=").append(mClientId);
+ b.append("}");
+ return b.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mClientId);
+ }
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index 7c11ed4..63a71e2 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -17,6 +17,7 @@
package android.media.tv.tunerresourcemanager;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -27,6 +28,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
/**
@@ -60,11 +63,25 @@
private static final String TAG = "TunerResourceManager";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- public static final int INVALID_FRONTEND_ID = -1;
- public static final int INVALID_CAS_SESSION_RESOURCE_ID = -1;
- public static final int INVALID_LNB_ID = -1;
- public static final int INVALID_TV_INPUT_DEVICE_ID = -1;
- public static final int INVALID_TV_INPUT_PORT_ID = -1;
+ public static final int INVALID_RESOURCE_HANDLE = -1;
+ /**
+ * Tuner resource type to help generate resource handle
+ */
+ @IntDef({
+ TUNER_RESOURCE_TYPE_FRONTEND,
+ TUNER_RESOURCE_TYPE_DEMUX,
+ TUNER_RESOURCE_TYPE_DESCRAMBLER,
+ TUNER_RESOURCE_TYPE_LNB,
+ TUNER_RESOURCE_TYPE_CAS_SESSION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TunerResourceType {}
+
+ public static final int TUNER_RESOURCE_TYPE_FRONTEND = 0;
+ public static final int TUNER_RESOURCE_TYPE_DEMUX = 1;
+ public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2;
+ public static final int TUNER_RESOURCE_TYPE_LNB = 3;
+ public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4;
private final ITunerResourceManager mService;
private final int mUserId;
@@ -260,6 +277,71 @@
}
/**
+ * Requests a Tuner Demux resource.
+ *
+ * <p>There are three possible scenarios:
+ * <ul>
+ * <li>If there is Demux available, the API would send the handle back.
+ *
+ * <li>If no Demux is available but the current request has a higher priority than other uses of
+ * demuxes, the API will send {@link IResourcesReclaimListener#onReclaimResources()} to the
+ * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and
+ * notify the holder of its resource loss.
+ *
+ * <li>If no Demux system can be granted, the API would return false.
+ * <ul>
+ *
+ * @param request {@link TunerDemuxRequest} information of the current request.
+ * @param demuxHandle a one-element array to return the granted Demux handle.
+ * If no Demux granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
+ *
+ * @return true if there is Demux granted.
+ */
+ public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) {
+ boolean result = false;
+ try {
+ result = mService.requestDemux(request, demuxHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return result;
+ }
+
+ /**
+ * Requests a Tuner Descrambler resource.
+ *
+ * <p>There are three possible scenarios:
+ * <ul>
+ * <li>If there is Descrambler available, the API would send the handle back.
+ *
+ * <li>If no Descrambler is available but the current request has a higher priority than other
+ * uses of descramblers, the API will send
+ * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
+ * handle the resource reclaim on the holder of lower priority and notify the holder of its
+ * resource loss.
+ *
+ * <li>If no Descrambler system can be granted, the API would return false.
+ * <ul>
+ *
+ * @param request {@link TunerDescramblerRequest} information of the current request.
+ * @param descramblerHandle a one-element array to return the granted Descrambler handle.
+ * If no Descrambler granted, this will return
+ * {@link #INVALID_RESOURCE_HANDLE}.
+ *
+ * @return true if there is Descrambler granted.
+ */
+ public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
+ @NonNull int[] descramblerHandle) {
+ boolean result = false;
+ try {
+ result = mService.requestDescrambler(request, descramblerHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return result;
+ }
+
+ /**
* Requests a CAS session resource.
*
* <p>There are three possible scenarios:
@@ -345,6 +427,36 @@
}
/**
+ * Notifies the TRM that the Demux with the given handle has been released.
+ *
+ * <p>Client must call this whenever it releases an Demux.
+ *
+ * @param demuxHandle the handle of the released Tuner Demux.
+ */
+ public void releaseDemux(int demuxHandle) {
+ try {
+ mService.releaseDemux(demuxHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notifies the TRM that the Descrambler with the given handle has been released.
+ *
+ * <p>Client must call this whenever it releases an Descrambler.
+ *
+ * @param descramblerHandle the handle of the released Tuner Descrambler.
+ */
+ public void releaseDescrambler(int descramblerHandle) {
+ try {
+ mService.releaseDescrambler(descramblerHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Notifies the TRM that the given Cas session has been released.
*
* <p>Client must call this whenever it releases a Cas session.
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 679f18a..bbeb451 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -138,11 +138,13 @@
jfieldID filterContext;
jfieldID timeFilterContext;
jfieldID descramblerContext;
- jfieldID dvrContext;
+ jfieldID dvrRecorderContext;
+ jfieldID dvrPlaybackContext;
jmethodID frontendInitID;
jmethodID filterInitID;
jmethodID timeFilterInitID;
- jmethodID dvrInitID;
+ jmethodID dvrRecorderInitID;
+ jmethodID dvrPlaybackInitID;
jmethodID onFrontendEventID;
jmethodID onFilterStatusID;
jmethodID onFilterEventID;
@@ -1333,7 +1335,7 @@
return timeFilterObj;
}
-jobject JTuner::openDvr(DvrType type, int bufferSize) {
+jobject JTuner::openDvr(DvrType type, jlong bufferSize) {
ALOGD("JTuner::openDvr");
if (mDemux == NULL) {
if (openDemux() != Result::SUCCESS) {
@@ -1342,24 +1344,38 @@
}
sp<IDvr> iDvrSp;
sp<DvrCallback> callback = new DvrCallback();
- mDemux->openDvr(type, bufferSize, callback,
- [&](Result, const sp<IDvr>& dvr) {
+ Result res;
+ mDemux->openDvr(type, (uint32_t) bufferSize, callback,
+ [&](Result r, const sp<IDvr>& dvr) {
+ res = r;
iDvrSp = dvr;
});
- if (iDvrSp == NULL) {
+ if (res != Result::SUCCESS || iDvrSp == NULL) {
return NULL;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject dvrObj =
- env->NewObject(
- env->FindClass("android/media/tv/tuner/dvr/Dvr"),
- gFields.dvrInitID,
- mObject);
- sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
- dvrSp->incStrong(dvrObj);
- env->SetLongField(dvrObj, gFields.dvrContext, (jlong)dvrSp.get());
+ jobject dvrObj;
+ if (type == DvrType::RECORD) {
+ dvrObj =
+ env->NewObject(
+ env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"),
+ gFields.dvrRecorderInitID,
+ mObject);
+ sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
+ dvrSp->incStrong(dvrObj);
+ env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrSp.get());
+ } else {
+ dvrObj =
+ env->NewObject(
+ env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"),
+ gFields.dvrPlaybackInitID,
+ mObject);
+ sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
+ dvrSp->incStrong(dvrObj);
+ env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrSp.get());
+ }
callback->setDvr(dvrObj);
@@ -1394,6 +1410,10 @@
return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext);
}
+static uint32_t getResourceIdFromHandle(jint handle) {
+ return (handle & 0x00ff0000) >> 16;
+}
+
static DemuxPid getDemuxPid(int pidType, int pid) {
DemuxPid demuxPid;
if ((int)pidType == 1) {
@@ -1883,7 +1903,11 @@
}
static sp<Dvr> getDvr(JNIEnv *env, jobject dvr) {
- return (Dvr *)env->GetLongField(dvr, gFields.dvrContext);
+ bool isRecorder =
+ env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"));
+ jfieldID fieldId =
+ isRecorder ? gFields.dvrRecorderContext : gFields.dvrPlaybackContext;
+ return (Dvr *)env->GetLongField(dvr, fieldId);
}
static void android_media_tv_Tuner_native_init(JNIEnv *env) {
@@ -1924,9 +1948,13 @@
gFields.descramblerInitID =
env->GetMethodID(descramblerClazz, "<init>", "()V");
- jclass dvrClazz = env->FindClass("android/media/tv/tuner/dvr/Dvr");
- gFields.dvrContext = env->GetFieldID(dvrClazz, "mNativeContext", "J");
- gFields.dvrInitID = env->GetMethodID(dvrClazz, "<init>", "()V");
+ jclass dvrRecorderClazz = env->FindClass("android/media/tv/tuner/dvr/DvrRecorder");
+ gFields.dvrRecorderContext = env->GetFieldID(dvrRecorderClazz, "mNativeContext", "J");
+ gFields.dvrRecorderInitID = env->GetMethodID(dvrRecorderClazz, "<init>", "()V");
+
+ jclass dvrPlaybackClazz = env->FindClass("android/media/tv/tuner/dvr/DvrPlayback");
+ gFields.dvrPlaybackContext = env->GetFieldID(dvrPlaybackClazz, "mNativeContext", "J");
+ gFields.dvrPlaybackInitID = env->GetMethodID(dvrPlaybackClazz, "<init>", "()V");
jclass linearBlockClazz = env->FindClass("android/media/MediaCodec$LinearBlock");
gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "<init>", "()V");
@@ -1944,8 +1972,10 @@
return tuner->getFrontendIds();
}
-static jobject android_media_tv_Tuner_open_frontend_by_id(JNIEnv *env, jobject thiz, jint id) {
+static jobject android_media_tv_Tuner_open_frontend_by_handle(
+ JNIEnv *env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
+ uint32_t id = getResourceIdFromHandle(handle);
return tuner->openFrontendById(id);
}
@@ -2021,8 +2051,9 @@
return tuner->getLnbIds();
}
-static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz, jint id) {
+static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
+ uint32_t id = getResourceIdFromHandle(handle);
return tuner->openLnbById(id);
}
@@ -2654,13 +2685,15 @@
}
static jobject android_media_tv_Tuner_open_dvr_recorder(
- JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) {
- return NULL;
+ JNIEnv* env, jobject thiz, jlong bufferSize) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openDvr(DvrType::RECORD, bufferSize);
}
static jobject android_media_tv_Tuner_open_dvr_playback(
- JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) {
- return NULL;
+ JNIEnv* env, jobject thiz, jlong bufferSize) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openDvr(DvrType::PLAYBACK, bufferSize);
}
static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) {
@@ -2714,11 +2747,13 @@
}
static int android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
+
sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
if (dvrSp == NULL) {
ALOGD("Failed to start dvr: dvr not found");
return false;
}
+
Result result = dvrSp->start();
return (int) result;
}
@@ -2896,8 +2931,8 @@
{ "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
{ "nativeGetFrontendIds", "()Ljava/util/List;",
(void *)android_media_tv_Tuner_get_frontend_ids },
- { "nativeOpenFrontendById", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
- (void *)android_media_tv_Tuner_open_frontend_by_id },
+ { "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
+ (void *)android_media_tv_Tuner_open_frontend_by_handle },
{ "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -2922,8 +2957,8 @@
(void *)android_media_tv_Tuner_open_time_filter },
{ "nativeGetLnbIds", "()Ljava/util/List;",
(void *)android_media_tv_Tuner_get_lnb_ids },
- { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Lnb;",
- (void *)android_media_tv_Tuner_open_lnb_by_id },
+ { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;",
+ (void *)android_media_tv_Tuner_open_lnb_by_handle },
{ "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;",
(void *)android_media_tv_Tuner_open_lnb_by_name },
{ "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Descrambler;",
@@ -2968,7 +3003,7 @@
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_descrambler },
};
-static const JNINativeMethod gDvrMethods[] = {
+static const JNINativeMethod gDvrRecorderMethods[] = {
{ "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_attach_filter },
{ "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
@@ -2980,14 +3015,22 @@
{ "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
{ "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
-};
-
-static const JNINativeMethod gDvrRecorderMethods[] = {
{ "nativeWrite", "(J)J", (void *)android_media_tv_Tuner_write_dvr },
{ "nativeWrite", "([BJJ)J", (void *)android_media_tv_Tuner_write_dvr_to_array },
};
static const JNINativeMethod gDvrPlaybackMethods[] = {
+ { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
+ (void *)android_media_tv_Tuner_attach_filter },
+ { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
+ (void *)android_media_tv_Tuner_detach_filter },
+ { "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
+ (void *)android_media_tv_Tuner_configure_dvr },
+ { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr },
+ { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
+ { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
+ { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
+ { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
{ "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr },
{ "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array },
};
@@ -3028,13 +3071,6 @@
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/dvr/Dvr",
- gDvrMethods,
- NELEM(gDvrMethods)) != JNI_OK) {
- ALOGE("Failed to register dvr native methods");
- return false;
- }
- if (AndroidRuntime::registerNativeMethods(
env, "android/media/tv/tuner/dvr/DvrRecorder",
gDvrRecorderMethods,
NELEM(gDvrRecorderMethods)) != JNI_OK) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 3b8682f..5d2bba6 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -184,7 +184,7 @@
jobject openFilter(DemuxFilterType type, int bufferSize);
jobject openTimeFilter();
jobject openDescrambler();
- jobject openDvr(DvrType type, int bufferSize);
+ jobject openDvr(DvrType type, jlong bufferSize);
protected:
Result openDemux();
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 83f3b6e..efeb335 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -120,8 +120,9 @@
}
if (mCaptureThread != 0) {
+ sp<CaptureThread> t = mCaptureThread;
mCaptureLock.unlock();
- mCaptureThread->requestExitAndWait();
+ t->requestExitAndWait();
mCaptureLock.lock();
}
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 1362433..f9a77f4 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -196,7 +196,6 @@
callbackInfo->visualizer_ref,
NATIVE_EVENT_PCM_CAPTURE,
samplingrate,
- 0,
jArray);
}
}
@@ -217,7 +216,6 @@
callbackInfo->visualizer_ref,
NATIVE_EVENT_FFT_CAPTURE,
samplingrate,
- 0,
jArray);
}
}
@@ -286,7 +284,7 @@
// Get the postEvent method
fields.midPostNativeEvent = env->GetStaticMethodID(
fields.clazzEffect,
- "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ "postEventFromNative", "(Ljava/lang/Object;II[B)V");
if (fields.midPostNativeEvent == NULL) {
ALOGE("Can't find Visualizer.%s", "postEventFromNative");
return;
@@ -343,7 +341,7 @@
fields.midPostNativeEvent,
callbackInfo->visualizer_ref,
NATIVE_EVENT_SERVER_DIED,
- 0, 0, NULL);
+ 0, NULL);
}
}
diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h
index 15b39f2..30ad220 100644
--- a/media/jni/soundpool/StreamManager.h
+++ b/media/jni/soundpool/StreamManager.h
@@ -70,9 +70,10 @@
static int staticFunction(void *data) {
JavaThread *jt = static_cast<JavaThread *>(data);
jt->mF();
+ jt->mIsClosed = true; // set the flag that we are closed
+ // now before we allow the destructor to execute;
+ // otherwise there may be a use after free.
jt->mPromise.set_value();
- jt->mIsClosed = true; // publicly inform that we are closed
- // after we have accessed all variables.
return 0;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 6354ccd..ebd7658 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -284,12 +284,12 @@
ICameraDeviceCallbacks dummyCallbacks = new DummyCameraDeviceCallbacks();
String clientPackageName = getContext().getPackageName();
- String clientFeatureId = getContext().getFeatureId();
+ String clientAttributionTag = getContext().getAttributionTag();
ICameraDeviceUser cameraUser =
mUtils.getCameraService().connectDevice(
dummyCallbacks, String.valueOf(cameraId),
- clientPackageName, clientFeatureId,
+ clientPackageName, clientAttributionTag,
ICameraService.USE_CALLING_UID);
assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 466c5f4..bf3e746 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -238,12 +238,12 @@
ICameraDeviceCallbacks.Stub dummyCallbacks = new DummyCameraDeviceCallbacks();
String clientPackageName = getContext().getPackageName();
- String clientFeatureId = getContext().getFeatureId();
+ String clientAttributionTag = getContext().getAttributionTag();
mMockCb = spy(dummyCallbacks);
mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId,
- clientPackageName, clientFeatureId, ICameraService.USE_CALLING_UID);
+ clientPackageName, clientAttributionTag, ICameraService.USE_CALLING_UID);
assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser);
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
diff --git a/media/tests/MediaRouter/AndroidManifest.xml b/media/tests/MediaRouter/AndroidManifest.xml
index 9546500..d9806f3 100644
--- a/media/tests/MediaRouter/AndroidManifest.xml
+++ b/media/tests/MediaRouter/AndroidManifest.xml
@@ -19,7 +19,7 @@
<application android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
- <service android:name=".SampleMediaRoute2ProviderService"
+ <service android:name=".StubMediaRoute2ProviderService"
android:exported="true">
<intent-filter>
<action android:name="android.media.MediaRoute2ProviderService" />
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 230b9e4..a97baaf 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -22,16 +22,16 @@
import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SPECIAL;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID1;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID2;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_NAME2;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.VOLUME_MAX;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SPECIAL;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID1;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID2;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_NAME2;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.VOLUME_MAX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -115,7 +115,7 @@
// unregister callbacks
clearCallbacks();
- SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance();
+ StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance();
if (instance != null) {
instance.setProxy(null);
}
@@ -161,8 +161,8 @@
MediaRoute2Info routeToRemove = routes.get(ROUTE_ID2);
- SampleMediaRoute2ProviderService sInstance =
- SampleMediaRoute2ProviderService.getInstance();
+ StubMediaRoute2ProviderService sInstance =
+ StubMediaRoute2ProviderService.getInstance();
assertNotNull(sInstance);
sInstance.removeRoute(ROUTE_ID2);
assertTrue(removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -413,12 +413,12 @@
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
- SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance();
+ StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance();
assertNotNull(instance);
final List<Long> requestIds = new ArrayList<>();
final CountDownLatch onSetRouteVolumeLatch = new CountDownLatch(1);
- instance.setProxy(new SampleMediaRoute2ProviderService.Proxy() {
+ instance.setProxy(new StubMediaRoute2ProviderService.Proxy() {
@Override
public void onSetRouteVolume(String routeId, int volume, long requestId) {
requestIds.add(requestId);
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
similarity index 98%
rename from media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
rename to media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
index f05d8ad..6d46ba5 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
@@ -36,7 +36,7 @@
import javax.annotation.concurrent.GuardedBy;
-public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService {
+public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
private static final String TAG = "SampleMR2ProviderSvc";
private static final Object sLock = new Object();
@@ -74,7 +74,7 @@
private int mNextSessionId = 1000;
@GuardedBy("sLock")
- private static SampleMediaRoute2ProviderService sInstance;
+ private static StubMediaRoute2ProviderService sInstance;
private Proxy mProxy;
private void initializeRoutes() {
@@ -127,7 +127,7 @@
mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
}
- public static SampleMediaRoute2ProviderService getInstance() {
+ public static StubMediaRoute2ProviderService getInstance() {
synchronized (sLock) {
return sInstance;
}
diff --git a/packages/CarSystemUI/proguard.flags b/packages/CarSystemUI/proguard.flags
index a81c7e0..66cbf26 100644
--- a/packages/CarSystemUI/proguard.flags
+++ b/packages/CarSystemUI/proguard.flags
@@ -1,3 +1,4 @@
-keep class com.android.systemui.CarSystemUIFactory
+-keep class com.android.car.notification.headsup.animationhelper.**
-include ../SystemUI/proguard.flags
diff --git a/packages/CarSystemUI/res/drawable/headsup_scrim_bottom.xml b/packages/CarSystemUI/res/drawable/headsup_scrim_bottom.xml
new file mode 100644
index 0000000..1724ef0
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/headsup_scrim_bottom.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.
+ -->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:startColor="@android:color/black"
+ android:endColor="@android:color/transparent"
+ android:angle="90" />
+</shape>
diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
new file mode 100644
index 0000000..caf1677
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
@@ -0,0 +1,50 @@
+<?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.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/notification_headsup"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/gradient_edge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/>
+
+ <View
+ android:id="@+id/scrim"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:background="@drawable/headsup_scrim_bottom"
+ app:layout_constraintBottom_toBottomOf="@+id/gradient_edge"
+ app:layout_constraintTop_toTopOf="parent"/>
+
+ <FrameLayout
+ android:id="@+id/headsup_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/headsup_notification_top_margin"
+ app:layout_constraintEnd_toStartOf="parent"
+ app:layout_constraintStart_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index aaa65de..2077e77 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -34,6 +34,13 @@
<!-- Whether heads-up notifications should be shown when shade is open. -->
<bool name="config_enableHeadsUpNotificationWhenNotificationShadeOpen">true</bool>
+ <!-- Whether heads-up notifications should be shown on the bottom. If false, heads-up
+ notifications will be shown pushed to the top of their parent container. If true, they will
+ be shown pushed to the bottom of their parent container. If true, then should override
+ config_headsUpNotificationAnimationHelper to use a different AnimationHelper, such as
+ com.android.car.notification.headsup.animationhelper.
+ CarHeadsUpNotificationBottomAnimationHelper. -->
+ <bool name="config_showHeadsUpNotificationOnBottom">false</bool>
<bool name="config_hideNavWhenKeyguardBouncerShown">true</bool>
<bool name="config_enablePersistentDockedActivity">false</bool>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 140c075..14d5bd5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -24,13 +24,14 @@
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
-import com.android.systemui.car.CarNotificationInterruptionStateProvider;
import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
import com.android.systemui.stackdivider.DividerModule;
@@ -41,7 +42,6 @@
import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
@@ -67,10 +67,6 @@
@Module(includes = {DividerModule.class})
abstract class CarSystemUIModule {
- @Binds
- abstract NotificationInterruptionStateProvider bindNotificationInterruptionStateProvider(
- CarNotificationInterruptionStateProvider notificationInterruptionStateProvider);
-
@Singleton
@Provides
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
@@ -113,6 +109,10 @@
BatteryControllerImpl controllerImpl);
@Binds
+ @Singleton
+ public abstract QSFactory provideQSFactory(QSFactoryImpl qsFactoryImpl);
+
+ @Binds
abstract DockManager bindDockManager(DockManagerImpl dockManager);
@Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
deleted file mode 100644
index 447e579..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.car;
-
-import android.content.Context;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.policy.BatteryController;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
-@Singleton
-public class CarNotificationInterruptionStateProvider extends
- NotificationInterruptionStateProvider {
-
- @Inject
- public CarNotificationInterruptionStateProvider(Context context,
- NotificationFilter filter,
- StatusBarStateController stateController,
- BatteryController batteryController) {
- super(context, filter, stateController, batteryController);
- }
-
- @Override
- public boolean shouldHeadsUp(NotificationEntry entry) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded. Ensure this by not pinning any
- // notification if the shade is already opened.
- if (!getPresenter().isPresenterFullyCollapsed()) {
- return false;
- }
-
- return super.shouldHeadsUp(entry);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
index 689d2d5..53e5d9f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
@@ -60,6 +60,8 @@
mCarDeviceProvisionedController = deviceProvisionedController;
mCarStatusBarLazy = carStatusBarLazy;
+ boolean showOnBottom = resources.getBoolean(R.bool.config_showHeadsUpNotificationOnBottom);
+
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT,
@@ -68,11 +70,13 @@
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
- lp.gravity = Gravity.TOP;
+ lp.gravity = showOnBottom ? Gravity.BOTTOM : Gravity.TOP;
lp.setTitle("HeadsUpNotification");
- mWindow = (ViewGroup) LayoutInflater.from(context)
- .inflate(R.layout.headsup_container, null, false);
+ int layoutId = showOnBottom
+ ? R.layout.headsup_container_bottom
+ : R.layout.headsup_container;
+ mWindow = (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null, false);
windowManager.addView(mWindow, lp);
mWindow.setVisibility(View.INVISIBLE);
mHeadsUpContentFrame = mWindow.findViewById(R.id.headsup_content);
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index b63162b..3ee92bd7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -238,10 +238,12 @@
}
buildNavBarContent();
- // If the UI was rebuilt (day/night change) while the keyguard was up we need to
- // correctly respect that state.
+ // If the UI was rebuilt (day/night change or user change) while the keyguard was up we need
+ // to correctly respect that state.
if (mKeyguardStateControllerLazy.get().isShowing()) {
mCarNavigationBarController.showAllKeyguardButtons(isDeviceSetupForUser());
+ } else {
+ mCarNavigationBarController.hideAllKeyguardButtons(isDeviceSetupForUser());
}
// Upon restarting the Navigation Bar, CarFacetButtonController should immediately apply the
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 b2e2104..411f14d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -95,13 +95,15 @@
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -249,7 +251,7 @@
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
NotificationAlertingManager notificationAlertingManager,
@@ -335,7 +337,7 @@
remoteInputQuickSettingsDisabler,
notificationGutsManager,
notificationLogger,
- notificationInterruptionStateProvider,
+ notificationInterruptStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
notificationAlertingManager,
@@ -488,6 +490,22 @@
.isCurrentUserSetupInProgress();
}
});
+
+ mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public boolean suppressInterruptions(NotificationEntry entry) {
+ // Because space is usually constrained in the auto use-case, there should not be a
+ // pinned notification when the shade has been expanded.
+ // Ensure this by not allowing any interruptions (ie: pinning any notifications) if
+ // the shade is already opened.
+ return !getPresenter().isPresenterFullyCollapsed();
+ }
+ });
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 4754118..160268b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -62,13 +62,13 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationRowModule;
@@ -146,7 +146,7 @@
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
NotificationAlertingManager notificationAlertingManager,
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
index c04e47f..f2748b8 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
@@ -46,8 +46,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import dagger.Lazy;
-
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -68,12 +66,8 @@
@Mock
private ButtonSelectionStateListener mButtonSelectionStateListener;
@Mock
- private Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
- @Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private Lazy<NavigationBarController> mNavigationBarControllerLazy;
- @Mock
private NavigationBarController mNavigationBarController;
@Mock
private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@@ -89,13 +83,11 @@
mCarNavigationBar = new CarNavigationBar(mContext, mCarNavigationBarController,
mWindowManager, mDeviceProvisionedController, new CommandQueue(mContext),
mAutoHideController, mButtonSelectionStateListener, mHandler,
- mKeyguardStateControllerLazy, mNavigationBarControllerLazy,
+ () -> mKeyguardStateController, () -> mNavigationBarController,
mSuperStatusBarViewFactory, mButtonSelectionStateController);
StatusBarWindowView statusBarWindowView = (StatusBarWindowView) LayoutInflater.from(
mContext).inflate(R.layout.super_status_bar, /* root= */ null);
when(mSuperStatusBarViewFactory.getStatusBarWindowView()).thenReturn(statusBarWindowView);
- when(mNavigationBarControllerLazy.get()).thenReturn(mNavigationBarController);
- when(mKeyguardStateControllerLazy.get()).thenReturn(mKeyguardStateController);
when(mKeyguardStateController.isShowing()).thenReturn(false);
mDependency.injectMockDependency(WindowManager.class);
// Needed to inflate top navigation bar.
@@ -119,4 +111,44 @@
verify(mButtonSelectionStateListener).onTaskStackChanged();
}
+
+ @Test
+ public void restartNavBars_newUserNotSetupWithKeyguardShowing_showsKeyguardButtons() {
+ ArgumentCaptor<CarDeviceProvisionedController.DeviceProvisionedListener>
+ deviceProvisionedCallbackCaptor = ArgumentCaptor.forClass(
+ CarDeviceProvisionedController.DeviceProvisionedListener.class);
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+ mCarNavigationBar.start();
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ // switching the currentUserSetup value to force restart the navbars.
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
+ verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
+
+ deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
+ waitForIdleSync(mHandler);
+
+ verify(mCarNavigationBarController).showAllKeyguardButtons(false);
+ }
+
+ @Test
+ public void restartNavbars_newUserIsSetupWithKeyguardHidden_hidesKeyguardButtons() {
+ ArgumentCaptor<CarDeviceProvisionedController.DeviceProvisionedListener>
+ deviceProvisionedCallbackCaptor = ArgumentCaptor.forClass(
+ CarDeviceProvisionedController.DeviceProvisionedListener.class);
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+ mCarNavigationBar.start();
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ // switching the currentUserSetup value to force restart the navbars.
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
+ verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
+ deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
+ waitForIdleSync(mHandler);
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ deviceProvisionedCallbackCaptor.getValue().onUserSetupChanged();
+ waitForIdleSync(mHandler);
+
+ verify(mCarNavigationBarController).hideAllKeyguardButtons(true);
+ }
}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index f93faeb..ace50f3 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -545,7 +545,7 @@
bundle.putString(META_DATA_PREFERENCE_KEYHINT, key);
}
try {
- return provider.call(context.getPackageName(), context.getFeatureId(),
+ return provider.call(context.getPackageName(), context.getAttributionTag(),
uri.getAuthority(), method, uri.toString(), bundle);
} catch (RemoteException e) {
return null;
diff --git a/packages/SettingsLib/res/drawable/ic_media_group_device.xml b/packages/SettingsLib/res/drawable/ic_media_group_device.xml
new file mode 100644
index 0000000..ba5e651
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_media_group_device.xml
@@ -0,0 +1,32 @@
+<!--
+ 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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
+ android:fillColor="#000000"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e8e1d0b..a797066 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1039,7 +1039,7 @@
<!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
<string name="accessibility_display_daltonizer_preference_title">Color correction</string>
<!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
- <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with colorblindness see more accurate colors</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle"><![CDATA[Color correction allows you to adjust how colors are displayed on your device]]></string>
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 18eb187..3c78560 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -218,7 +218,7 @@
}
public boolean supportsHighQualityAudio(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
if (bluetoothDevice == null) {
return false;
}
@@ -227,7 +227,7 @@
}
public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
if (bluetoothDevice == null) {
return false;
}
@@ -253,7 +253,7 @@
}
public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) {
- BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
if (bluetoothDevice == null) {
return;
}
@@ -272,7 +272,7 @@
}
public String getHighQualityAudioOptionLabel(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec;
if (bluetoothDevice == null || !supportsHighQualityAudio(device)
|| getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index b725ba5..85fa988 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -15,11 +15,17 @@
*/
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.TYPE_GROUP;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -51,7 +57,23 @@
public Drawable getIcon() {
//TODO(b/120669861): Return remote device icon uri once api is ready.
return BluetoothUtils.buildBtRainbowDrawable(mContext,
- mContext.getDrawable(R.drawable.ic_media_device), getId().hashCode());
+ mContext.getDrawable(getDrawableResId()), getId().hashCode());
+ }
+
+ @VisibleForTesting
+ int getDrawableResId() {
+ int resId;
+ switch (mRouteInfo.getType()) {
+ case TYPE_GROUP:
+ resId = R.drawable.ic_media_group_device;
+ break;
+ case TYPE_REMOTE_TV:
+ case TYPE_REMOTE_SPEAKER:
+ default:
+ resId = R.drawable.ic_media_device;
+ break;
+ }
+ return resId;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 90f55dd6..9ae9b4a4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -49,11 +49,13 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({MediaDeviceState.STATE_CONNECTED,
MediaDeviceState.STATE_CONNECTING,
- MediaDeviceState.STATE_DISCONNECTED})
+ MediaDeviceState.STATE_DISCONNECTED,
+ MediaDeviceState.STATE_CONNECTING_FAILED})
public @interface MediaDeviceState {
- int STATE_CONNECTED = 1;
- int STATE_CONNECTING = 2;
- int STATE_DISCONNECTED = 3;
+ int STATE_CONNECTED = 0;
+ int STATE_CONNECTING = 1;
+ int STATE_DISCONNECTED = 2;
+ int STATE_CONNECTING_FAILED = 3;
}
private final Collection<DeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
@@ -93,6 +95,15 @@
mCallbacks.remove(callback);
}
+ /**
+ * Creates a LocalMediaManager with references to given managers.
+ *
+ * It will obtain a {@link LocalBluetoothManager} by calling
+ * {@link LocalBluetoothManager#getInstance} and create an {@link InfoMediaManager} passing
+ * that bluetooth manager.
+ *
+ * It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter.
+ */
public LocalMediaManager(Context context, String packageName, Notification notification) {
mContext = context;
mPackageName = packageName;
@@ -108,14 +119,18 @@
new InfoMediaManager(context, packageName, notification, mLocalBluetoothManager);
}
- @VisibleForTesting
- LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
+ /**
+ * Creates a LocalMediaManager with references to given managers.
+ *
+ * It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter.
+ */
+ public LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
InfoMediaManager infoMediaManager, String packageName) {
mContext = context;
mLocalBluetoothManager = localBluetoothManager;
mInfoMediaManager = infoMediaManager;
mPackageName = packageName;
-
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
/**
@@ -128,6 +143,7 @@
final CachedBluetoothDevice cachedDevice =
((BluetoothMediaDevice) device).getCachedDevice();
if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
+ device.setState(MediaDeviceState.STATE_CONNECTING);
cachedDevice.connect();
return;
}
@@ -142,6 +158,7 @@
mCurrentConnectedDevice.disconnect();
}
+ device.setState(MediaDeviceState.STATE_CONNECTING);
if (TextUtils.isEmpty(mPackageName)) {
mInfoMediaManager.connectDeviceWithoutPackageName(device);
} else {
@@ -423,6 +440,7 @@
MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id);
connectDevice = connectDevice != null
? connectDevice : updateCurrentConnectedDevice();
+ connectDevice.setState(MediaDeviceState.STATE_CONNECTED);
if (connectDevice == mCurrentConnectedDevice) {
Log.d(TAG, "onConnectedDeviceChanged() this device all ready connected!");
@@ -440,6 +458,11 @@
@Override
public void onRequestFailed(int reason) {
+ for (MediaDevice device : mMediaDevices) {
+ if (device.getState() == MediaDeviceState.STATE_CONNECTING) {
+ device.setState(MediaDeviceState.STATE_CONNECTING_FAILED);
+ }
+ }
dispatchOnRequestFailed(reason);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 33c3d7e..39e6a12 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -51,6 +51,7 @@
int mType;
private int mConnectedRecord;
+ private int mState;
protected final Context mContext;
protected final MediaRoute2Info mRouteInfo;
@@ -200,6 +201,22 @@
}
/**
+ * Set current device's state
+ */
+ public void setState(@LocalMediaManager.MediaDeviceState int state) {
+ mState = state;
+ }
+
+ /**
+ * Get current device's state
+ *
+ * @return state of device
+ */
+ public @LocalMediaManager.MediaDeviceState int getState() {
+ return mState;
+ }
+
+ /**
* Rules:
* 1. If there is one of the connected devices identified as a carkit, this carkit will
* be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 166fbaa..af88723 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -15,11 +15,17 @@
*/
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -43,7 +49,18 @@
@Override
public String getName() {
- return mContext.getString(R.string.media_transfer_this_device_name);
+ CharSequence name;
+ switch (mRouteInfo.getType()) {
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ name = mRouteInfo.getName();
+ break;
+ case TYPE_BUILTIN_SPEAKER:
+ default:
+ name = mContext.getString(R.string.media_transfer_this_device_name);
+ break;
+ }
+ return name.toString();
}
@Override
@@ -54,7 +71,23 @@
@Override
public Drawable getIcon() {
return BluetoothUtils.buildBtRainbowDrawable(mContext,
- mContext.getDrawable(R.drawable.ic_smartphone), getId().hashCode());
+ mContext.getDrawable(getDrawableResId()), getId().hashCode());
+ }
+
+ @VisibleForTesting
+ int getDrawableResId() {
+ int resId;
+ switch (mRouteInfo.getType()) {
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ resId = com.android.internal.R.drawable.ic_bt_headphones_a2dp;
+ break;
+ case TYPE_BUILTIN_SPEAKER:
+ default:
+ resId = R.drawable.ic_smartphone;
+ break;
+ }
+ return resId;
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
index 68a3729..245b7843 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
@@ -8,7 +8,6 @@
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
-import android.app.AppOpsManager.OpFeatureEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -163,6 +162,6 @@
AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null));
return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
- new OpFeatureEntry(op, false, accessEvents, null)));
+ new AppOpsManager.AttributedOpEntry(op, false, accessEvents, null)));
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 3f8d758..cc9b931 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -7,7 +7,7 @@
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
-import android.app.AppOpsManager.OpFeatureEntry;
+import android.app.AppOpsManager.AttributedOpEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -18,8 +18,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.LongSparseArray;
-import android.util.LongSparseLongArray;
-import android.util.Pair;
import org.junit.Before;
import org.junit.Test;
@@ -164,6 +162,6 @@
AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, duration, null));
return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
- new OpFeatureEntry(op, false, accessEvents, null)));
+ new AttributedOpEntry(op, false, accessEvents, null)));
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index 77a67c2..685c834 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -16,6 +16,10 @@
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.TYPE_GROUP;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -86,4 +90,19 @@
assertThat(mInfoMediaDevice.getId()).isEqualTo(TEST_ID);
}
+
+ @Test
+ public void getDrawableResId_returnCorrectResId() {
+ when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_TV);
+
+ assertThat(mInfoMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_device);
+
+ when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+
+ assertThat(mInfoMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_device);
+
+ when(mRouteInfo.getType()).thenReturn(TYPE_GROUP);
+
+ assertThat(mInfoMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_group_device);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 18d8f14..559187d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -16,6 +16,8 @@
package com.android.settingslib.media;
+import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -27,6 +29,8 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -53,10 +57,13 @@
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalMediaManagerTest {
+ private static final String TEST_DEVICE_NAME_1 = "device_name_1";
+ private static final String TEST_DEVICE_NAME_2 = "device_name_2";
private static final String TEST_DEVICE_ID_1 = "device_id_1";
private static final String TEST_DEVICE_ID_2 = "device_id_2";
private static final String TEST_DEVICE_ID_3 = "device_id_3";
private static final String TEST_CURRENT_DEVICE_ID = "currentDevice_id";
+ private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
@Mock
private BluetoothMediaManager mBluetoothMediaManager;
@@ -72,10 +79,18 @@
private A2dpProfile mA2dpProfile;
@Mock
private LocalBluetoothProfileManager mLocalProfileManager;
+ @Mock
+ private MediaRouter2Manager mMediaRouter2Manager;
+ @Mock
+ private MediaRoute2Info mRouteInfo1;
+ @Mock
+ private MediaRoute2Info mRouteInfo2;
private Context mContext;
private LocalMediaManager mLocalMediaManager;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private InfoMediaDevice mInfoMediaDevice1;
+ private InfoMediaDevice mInfoMediaDevice2;
@Before
public void setUp() {
@@ -84,14 +99,20 @@
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
-
+ when(mRouteInfo1.getName()).thenReturn(TEST_DEVICE_NAME_1);
+ when(mRouteInfo1.getId()).thenReturn(TEST_DEVICE_ID_1);
+ when(mRouteInfo2.getName()).thenReturn(TEST_DEVICE_NAME_2);
+ when(mRouteInfo2.getId()).thenReturn(TEST_DEVICE_ID_2);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
+ mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+ TEST_PACKAGE_NAME);
+ mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
+ TEST_PACKAGE_NAME);
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
mInfoMediaManager, "com.test.packagename");
- mLocalMediaManager.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
@Test
@@ -123,6 +144,32 @@
}
@Test
+ public void connectDevice_deviceNotEqualCurrentConnectedDevice_isConnectingState() {
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+ mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
+
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.connectDevice(mInfoMediaDevice2);
+
+ assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTING);
+ }
+
+ @Test
+ public void connectDevice_deviceEqualCurrentConnectedDevice_notConnectingState() {
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+ mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
+
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.connectDevice(mInfoMediaDevice1);
+
+ assertThat(mInfoMediaDevice1.getState()).isNotEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTING);
+ }
+
+ @Test
public void connectDevice_bluetoothDeviceNotConnected_connectBluetoothDevice() {
final MediaDevice device = mock(BluetoothMediaDevice.class);
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
@@ -388,6 +435,21 @@
}
@Test
+ public void onConnectedDeviceChanged_isConnectedState() {
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_DISCONNECTED);
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
+
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTED);
+ }
+
+ @Test
public void onDeviceAttributesChanged_shouldDispatchDeviceListUpdate() {
mLocalMediaManager.registerCallback(mCallback);
@@ -397,6 +459,26 @@
}
@Test
+ public void onRequestFailed_checkDevicesState() {
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+ mInfoMediaDevice2.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTING);
+ assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTED);
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(REASON_UNKNOWN_ERROR);
+
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTING_FAILED);
+ assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTED);
+ }
+
+ @Test
public void getActiveMediaDevice_checkList() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 3f29b72..4b08387 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -448,4 +448,23 @@
assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
mContext.getResources().getString(R.string.unknown));
}
+
+ @Test
+ public void setState_verifyGetState() {
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index db984fb..4c5cd96 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -16,15 +16,23 @@
package com.android.settingslib.media;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
+import android.media.MediaRoute2Info;
import com.android.settingslib.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@@ -32,6 +40,9 @@
@RunWith(RobolectricTestRunner.class)
public class PhoneMediaDeviceTest {
+ @Mock
+ private MediaRoute2Info mInfo;
+
private Context mContext;
private PhoneMediaDevice mPhoneMediaDevice;
@@ -41,7 +52,7 @@
mContext = RuntimeEnvironment.application;
mPhoneMediaDevice =
- new PhoneMediaDevice(mContext, null, null, null);
+ new PhoneMediaDevice(mContext, null, mInfo, null);
}
@Test
@@ -58,4 +69,42 @@
assertThat(mPhoneMediaDevice.getSummary()).isEmpty();
}
+
+ @Test
+ public void getDrawableResId_returnCorrectResId() {
+ when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
+
+ assertThat(mPhoneMediaDevice.getDrawableResId())
+ .isEqualTo(com.android.internal.R.drawable.ic_bt_headphones_a2dp);
+
+ when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADSET);
+
+ assertThat(mPhoneMediaDevice.getDrawableResId())
+ .isEqualTo(com.android.internal.R.drawable.ic_bt_headphones_a2dp);
+
+ when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
+
+ assertThat(mPhoneMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_smartphone);
+ }
+
+ @Test
+ public void getName_returnCorrectName() {
+ final String deviceName = "test_name";
+
+ when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
+ when(mInfo.getName()).thenReturn(deviceName);
+
+ assertThat(mPhoneMediaDevice.getName())
+ .isEqualTo(deviceName);
+
+ when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADSET);
+
+ assertThat(mPhoneMediaDevice.getName())
+ .isEqualTo(deviceName);
+
+ when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
+
+ assertThat(mPhoneMediaDevice.getName())
+ .isEqualTo(mContext.getString(R.string.media_transfer_this_device_name));
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8fa98c8..d350d9d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -141,6 +141,8 @@
Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
Settings.Secure.UI_NIGHT_MODE,
+ Settings.Secure.DARK_THEME_CUSTOM_START_TIME,
+ Settings.Secure.DARK_THEME_CUSTOM_END_TIME,
Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
Settings.Secure.SKIP_DIRECTION,
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 75c5f95..4d33b62 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -28,6 +28,7 @@
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
@@ -235,7 +236,9 @@
VALIDATORS.put(Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.ODI_CAPTIONS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.UI_NIGHT_MODE, new InclusiveIntegerRangeValidator(0, 2));
+ VALIDATORS.put(Secure.UI_NIGHT_MODE, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.DARK_THEME_CUSTOM_START_TIME, NONE_NEGATIVE_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.DARK_THEME_CUSTOM_END_TIME, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Secure.GLOBAL_ACTIONS_PANEL_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.AWARE_LOCK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DISPLAY_DENSITY_FORCED, NON_NEGATIVE_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 71c7544..8d5c6e6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -207,4 +207,15 @@
static final Validator ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR =
new AccessibilityShortcutTargetListValidator();
+
+ static final Validator NONE_NEGATIVE_LONG_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(String value) {
+ try {
+ return Long.parseLong(value) >= 0;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 36bb8ef..b6e31d2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -75,6 +75,9 @@
sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON);
+ sBroadcastOnRestore.add(Settings.Secure.UI_NIGHT_MODE);
+ sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_START_TIME);
+ sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
}
private interface SettingsLookup {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 610165a..dab0505 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -199,7 +199,6 @@
Settings.Global.CERT_PIN_UPDATE_CONTENT_URL,
Settings.Global.CERT_PIN_UPDATE_METADATA_URL,
Settings.Global.COMPATIBILITY_MODE,
- Settings.Global.COMMON_CRITERIA_MODE,
Settings.Global.CONNECTIVITY_CHANGE_DELAY,
Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE,
Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
index bb9e6f6..9134d87 100644
--- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -277,6 +277,27 @@
}
@Test
+ public void testPositiveLongValidator_zero() {
+ assertTrue(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("0"));
+ }
+
+ @Test
+ public void testPositiveLongValidator_negative() {
+ assertFalse(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("-5"));
+ }
+
+
+ @Test
+ public void testPositiveLongValidator_positive() {
+ assertTrue(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("5"));
+ }
+
+ @Test
+ public void testPositiveLongValidator_floatFormat() {
+ assertFalse(SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR.validate("4.4756"));
+ }
+
+ @Test
public void testTTSListValidator_withNullInput_returnsFalse() {
assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(null));
}
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index cff958f..c036b04 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -12,6 +12,9 @@
"include-annotation": "android.platform.test.scenario.annotation.Scenario"
},
{
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
@@ -28,6 +31,9 @@
"include-annotation": "android.platform.test.scenario.annotation.Scenario"
},
{
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/packages/SystemUI/res/color/control_background.xml b/packages/SystemUI/res/color/control_background.xml
deleted file mode 100644
index 977310c..0000000
--- a/packages/SystemUI/res/color/control_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:color="@color/control_default_background" />
- <item android:color="@color/GM2_blue_200"
- android:alpha="0.2" />
-</selector>
diff --git a/packages/SystemUI/res/color/light_background.xml b/packages/SystemUI/res/color/light_background.xml
deleted file mode 100644
index 1299464..0000000
--- a/packages/SystemUI/res/color/light_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:color="@color/control_default_background" />
- <item android:color="@color/GM2_yellow_200"
- android:alpha="0.2" />
-</selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_background.xml b/packages/SystemUI/res/color/thermo_cool_background.xml
deleted file mode 100644
index 977310c..0000000
--- a/packages/SystemUI/res/color/thermo_cool_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:color="@color/control_default_background" />
- <item android:color="@color/GM2_blue_200"
- android:alpha="0.2" />
-</selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_background.xml b/packages/SystemUI/res/color/thermo_heat_background.xml
deleted file mode 100644
index 2709ebe..0000000
--- a/packages/SystemUI/res/color/thermo_heat_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:color="@color/control_default_background" />
- <item android:color="@color/GM2_red_200"
- android:alpha="0.2" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml b/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
index 64b57c5..3acebc1 100644
--- a/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
+++ b/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
@@ -14,10 +14,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:viewportHeight="24"
- android:viewportWidth="24"
- android:height="52dp"
- android:width="52dp">
- <path android:fillColor="#1A73E8"
- android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
-</vector>
\ No newline at end of file
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="24dp"
+ android:height="24dp">
+ <path
+ android:fillColor="#1A73E8"
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 374e3b1..c58f572 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -21,8 +21,9 @@
android:layout_weight="1"
android:layout_height="@dimen/control_height"
android:padding="@dimen/control_padding"
- android:clickable="true"
+ android:clickable="false"
android:focusable="true"
+ android:screenReaderFocusable="true"
android:layout_marginLeft="@dimen/control_base_item_margin"
android:layout_marginRight="@dimen/control_base_item_margin"
android:background="@drawable/control_background">
@@ -32,6 +33,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/control_padding_adjustment"
+ android:clickable="false"
+ android:focusable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -42,6 +45,8 @@
android:textAppearance="@style/TextAppearance.Control.Status"
android:paddingTop="@dimen/control_padding_adjustment"
android:paddingStart="@dimen/control_status_padding"
+ android:clickable="false"
+ android:focusable="false"
app:layout_constraintBottom_toBottomOf="@+id/icon"
app:layout_constraintStart_toEndOf="@+id/icon" />
@@ -52,6 +57,8 @@
android:textAppearance="@style/TextAppearance.Control.Status"
android:paddingTop="@dimen/control_padding_adjustment"
android:paddingStart="@dimen/control_status_padding"
+ android:clickable="false"
+ android:focusable="false"
app:layout_constraintBottom_toBottomOf="@+id/icon"
app:layout_constraintStart_toEndOf="@+id/status" />
@@ -62,6 +69,8 @@
android:textAppearance="@style/TextAppearance.Control.Title"
android:paddingLeft="@dimen/control_padding_adjustment"
android:paddingRight="@dimen/control_padding_adjustment"
+ android:clickable="false"
+ android:focusable="false"
app:layout_constraintBottom_toTopOf="@+id/subtitle"
app:layout_constraintStart_toStartOf="parent" />
@@ -73,6 +82,8 @@
android:paddingLeft="@dimen/control_padding_adjustment"
android:paddingRight="@dimen/control_padding_adjustment"
android:paddingBottom="@dimen/control_padding_adjustment"
+ android:clickable="false"
+ android:focusable="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index a76f961..d506e7e 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -58,13 +58,15 @@
android:elevation="@dimen/screenshot_preview_elevation"
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
- android:adjustViewBounds="true"/>
+ android:adjustViewBounds="true"
+ android:contentDescription="@string/screenshot_preview_description"/>
<FrameLayout
android:id="@+id/global_screenshot_dismiss_button"
android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
android:elevation="7dp"
- android:visibility="gone">
+ android:visibility="gone"
+ android:contentDescription="@string/screenshot_dismiss_ui_description">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 80c1ac8..56e2b06 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -225,4 +225,8 @@
<color name="control_default_background">@color/GM2_grey_900</color>
<color name="control_list_popup_background">@*android:color/background_floating_material_dark</color>
<color name="control_spinner_dropdown">@*android:color/foreground_material_dark</color>
+ <color name="control_enabled_light_background">@color/GM2_yellow_200</color>
+ <color name="control_enabled_thermo_heat_background">@color/GM2_red_200</color>
+ <color name="control_enabled_thermo_cool_background">@color/GM2_blue_200</color>
+ <color name="control_enabled_default_background">@color/GM2_blue_200</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 20cafd0..06e027d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -117,7 +117,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse
</string>
<!-- The tiles to display in QuickSettings -->
@@ -468,12 +468,18 @@
<!-- On debuggable builds, alert the user if SystemUI PSS goes over this number (in kb) -->
<integer name="watch_heap_limit">256000</integer>
+ <!-- Animation duration for resizing of PIP when entering/exiting. -->
+ <integer name="config_pipResizeAnimationDuration">425</integer>
+
<!-- Allow dragging the PIP to a location to close it -->
<bool name="config_pipEnableDismissDragToEdge">true</bool>
<!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
<bool name="config_pipEnableResizeForMenu">true</bool>
+ <!-- Allow PIP to enable round corner, see also R.dimen.pip_corner_radius -->
+ <bool name="config_pipEnableRoundCorner">false</bool>
+
<!-- SystemUI Plugins that can be loaded on user builds. -->
<string-array name="config_pluginWhitelist" translatable="false">
<item>com.android.systemui</item>
@@ -523,4 +529,7 @@
<!-- Flag to turn on the rendering of the above path or not -->
<bool name="config_enableDisplayCutoutProtection">false</bool>
+ <!-- Respect drawable/rounded.xml intrinsic size for multiple radius corner path customization -->
+ <bool name="config_roundedCornerMultipleRadius">false</bool>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 291db65..e45cbec 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -124,9 +124,6 @@
<!-- Increased height of a collapsed media notification in the status bar -->
<dimen name="notification_min_height_media">160dp</dimen>
- <!-- Increased height of a collapsed messaging notification in the status bar -->
- <dimen name="notification_min_height_messaging">118dp</dimen>
-
<!-- Height of a small notification in the status bar which was used before android N -->
<dimen name="notification_min_height_legacy">64dp</dimen>
@@ -1234,7 +1231,7 @@
<dimen name="control_height">106dp</dimen>
<dimen name="control_padding">12dp</dimen>
<dimen name="control_padding_adjustment">4dp</dimen>
- <dimen name="control_status_normal">12sp</dimen>
+ <dimen name="control_status_normal">14sp</dimen>
<dimen name="control_status_expanded">18sp</dimen>
<dimen name="control_base_item_margin">2dp</dimen>
<dimen name="control_status_padding">3dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 18fec29..93bafdb 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -233,6 +233,10 @@
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
your organization</string>
+ <!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
+ <string name="screenshot_dismiss_ui_description">Dismiss screenshot</string>
+ <!-- Content description indicating that tapping will open an app to view/edit the screenshot. [CHAR LIMIT=NONE] -->
+ <string name="screenshot_preview_description">Open screenshot</string>
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
<string name="screenrecord_name">Screen Recorder</string>
@@ -2631,10 +2635,7 @@
<string name="controls_favorite_default_title">Controls</string>
<!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
<string name="controls_favorite_subtitle">Choose controls for quick access</string>
- <!-- Controls management controls screen favorites header [CHAR LIMIT=50] -->
- <string name="controls_favorite_header_favorites">Favorites</string>
- <!-- Controls management controls screen all header [CHAR LIMIT=50] -->
- <string name="controls_favorite_header_all">All</string>
+
<!-- Controls management controls screen error on load message [CHAR LIMIT=60] -->
<string name="controls_favorite_load_error">The list of all controls could not be loaded.</string>
<!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4770910..20b88a1 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -677,7 +677,7 @@
<style name="TextAppearance.Control.Status">
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textSize">@dimen/control_text_size</item>
+ <item name="android:textSize">@dimen/control_status_normal</item>
<item name="android:textColor">@color/control_primary_text</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 4885ade..07bd3a0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -78,6 +78,9 @@
public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
// Winscope tracing is enabled
public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
+ // The Assistant gesture should be constrained. It is up to the launcher implementation to
+ // decide how to constrain it
+ public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -92,7 +95,8 @@
SYSUI_STATE_OVERVIEW_DISABLED,
SYSUI_STATE_HOME_DISABLED,
SYSUI_STATE_SEARCH_DISABLED,
- SYSUI_STATE_TRACING_ENABLED
+ SYSUI_STATE_TRACING_ENABLED,
+ SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED
})
public @interface SystemUiStateFlags {}
@@ -112,6 +116,8 @@
str.add((flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0 ? "a11y_click" : "");
str.add((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0 ? "a11y_long_click" : "");
str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
+ str.add((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0
+ ? "asst_gesture_constrain" : "");
return str.toString();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
index d33c653..29100ef 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
@@ -90,7 +90,7 @@
view.setTranslationX((surfaceControl.getWidth() - scale * viewSize.getWidth()) / 2);
view.setTranslationY((surfaceControl.getHeight() - scale * viewSize.getHeight()) / 2);
- mSurfaceControlViewHost.addView(view, layoutParams);
+ mSurfaceControlViewHost.setView(view, layoutParams);
}
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
index b813e21..7570c2c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
@@ -18,7 +18,11 @@
import android.app.WallpaperManager;
import android.content.Context;
+import android.os.IBinder;
+/**
+ * @see WallpaperManager
+ */
public class WallpaperManagerCompat {
private final WallpaperManager mWallpaperManager;
@@ -26,7 +30,10 @@
mWallpaperManager = context.getSystemService(WallpaperManager.class);
}
- public void setWallpaperZoomOut(float zoom) {
- mWallpaperManager.setWallpaperZoomOut(zoom);
+ /**
+ * @see WallpaperManager#setWallpaperZoomOut(IBinder, float)
+ */
+ public void setWallpaperZoomOut(IBinder windowToken, float zoom) {
+ mWallpaperManager.setWallpaperZoomOut(windowToken, zoom);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 3cf07d1..ba8a1a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,9 +19,7 @@
import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
-
import static com.android.systemui.DejankUtils.whitelistIpcs;
-
import static java.lang.Integer.max;
import android.app.Activity;
@@ -30,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
+import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.Looper;
@@ -512,8 +511,6 @@
boolean finish = false;
boolean strongAuth = false;
int eventSubtype = -1;
- mCurrentSecuritySelection = whitelistIpcs(() ->
- mSecurityModel.getSecurityMode(targetUserId));
if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
finish = true;
eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
@@ -521,8 +518,13 @@
finish = true;
eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
} else if (SecurityMode.None == mCurrentSecuritySelection) {
- finish = true; // no security required
- eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (SecurityMode.None == securityMode) {
+ finish = true; // no security required
+ eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ }
} else if (authenticated) {
switch (mCurrentSecuritySelection) {
case Pattern:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 4508fc7..90df124 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -379,7 +379,7 @@
if (DEBUG_SIM_STATES) {
Log.v(TAG, "onSubscriptionInfoChanged()");
List<SubscriptionInfo> sil = mSubscriptionManager
- .getActiveAndHiddenSubscriptionInfoList();
+ .getCompleteActiveSubscriptionInfoList();
if (sil != null) {
for (SubscriptionInfo subInfo : sil) {
Log.v(TAG, "SubInfo:" + subInfo);
@@ -433,10 +433,10 @@
public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
List<SubscriptionInfo> sil = mSubscriptionInfo;
if (sil == null || forceReload) {
- sil = mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList();
+ sil = mSubscriptionManager.getCompleteActiveSubscriptionInfoList();
}
if (sil == null) {
- // getActiveAndHiddenSubscriptionInfoList was null callers expect an empty list.
+ // getCompleteActiveSubscriptionInfoList was null callers expect an empty list.
mSubscriptionInfo = new ArrayList<SubscriptionInfo>();
} else {
mSubscriptionInfo = sil;
@@ -1086,7 +1086,7 @@
mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
} else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
final Message msg = mHandler.obtainMessage(
- MSG_TIMEZONE_UPDATE, intent.getStringExtra("time-zone"));
+ MSG_TIMEZONE_UPDATE, intent.getStringExtra(Intent.EXTRA_TIMEZONE));
mHandler.sendMessage(msg);
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
@@ -2740,6 +2740,8 @@
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver);
+
+ mHandler.removeCallbacksAndMessages(null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index a868cf5..b6152da 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -71,12 +71,11 @@
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -289,7 +288,6 @@
@Inject Lazy<NotificationLogger> mNotificationLogger;
@Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
@Inject Lazy<NotificationFilter> mNotificationFilter;
- @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
@@ -489,8 +487,6 @@
mProviders.put(NotificationViewHierarchyManager.class,
mNotificationViewHierarchyManager::get);
mProviders.put(NotificationFilter.class, mNotificationFilter::get);
- mProviders.put(NotificationInterruptionStateProvider.class,
- mNotificationInterruptionStateProvider::get);
mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index dbcdead..23fa645 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -203,6 +203,11 @@
}
}
+ @Override
+ public boolean shouldZoomOutWallpaper() {
+ return true;
+ }
+
private void waitForBackgroundRendering() {
synchronized (mMonitor) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index cab9f18..a8a3cae 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -48,7 +48,9 @@
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
+import android.graphics.drawable.VectorDrawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.HandlerThread;
@@ -125,8 +127,9 @@
private WindowManager mWindowManager;
private int mRotation;
private SecureSetting mColorInversionSetting;
- private boolean mPendingRotationChange;
private Handler mHandler;
+ private boolean mPendingRotationChange;
+ private boolean mIsRoundedCornerMultipleRadius;
private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
new CameraAvailabilityListener.CameraTransitionCallback() {
@@ -193,6 +196,8 @@
mRotation = mContext.getDisplay().getRotation();
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mIsRoundedCornerMultipleRadius = mContext.getResources().getBoolean(
+ R.bool.config_roundedCornerMultipleRadius);
updateRoundedCornerRadii();
setupDecorations();
setupCameraListener();
@@ -571,15 +576,22 @@
com.android.internal.R.dimen.rounded_corner_radius_top);
final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.rounded_corner_radius_bottom);
-
final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
|| mRoundedDefaultBottom != newRoundedDefaultBottom
|| mRoundedDefaultTop != newRoundedDefaultTop;
if (roundedCornersChanged) {
- mRoundedDefault = newRoundedDefault;
- mRoundedDefaultTop = newRoundedDefaultTop;
- mRoundedDefaultBottom = newRoundedDefaultBottom;
+ // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the
+ // max(width, height) size of drawable/rounded.xml instead of rounded_corner_radius
+ if (mIsRoundedCornerMultipleRadius) {
+ final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+ mRoundedDefault = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+ mRoundedDefaultTop = mRoundedDefaultBottom = mRoundedDefault;
+ } else {
+ mRoundedDefault = newRoundedDefault;
+ mRoundedDefaultTop = newRoundedDefaultTop;
+ mRoundedDefaultBottom = newRoundedDefaultBottom;
+ }
onTuningChanged(SIZE, null);
}
}
@@ -630,7 +642,8 @@
}
private boolean hasRoundedCorners() {
- return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0;
+ return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0
+ || mIsRoundedCornerMultipleRadius;
}
private boolean shouldShowRoundedCorner(@BoundsPosition int pos) {
@@ -725,7 +738,8 @@
private final Rect mBoundingRect = new Rect();
private final Path mBoundingPath = new Path();
// Don't initialize these yet because they may never exist
- private Rect mProtectionRect;
+ private RectF mProtectionRect;
+ private RectF mProtectionRectOrig;
private Path mProtectionPath;
private Path mProtectionPathOrig;
private Rect mTotalBounds = new Rect();
@@ -818,7 +832,11 @@
mProtectionPath = new Path();
}
mProtectionPathOrig.set(protectionPath);
- mProtectionRect = pathBounds;
+ if (mProtectionRectOrig == null) {
+ mProtectionRectOrig = new RectF();
+ mProtectionRect = new RectF();
+ }
+ mProtectionRectOrig.set(pathBounds);
}
void setShowProtection(boolean shouldShow) {
@@ -898,6 +916,7 @@
// Reset the protection path so we don't aggregate rotations
mProtectionPath.set(mProtectionPathOrig);
mProtectionPath.transform(m);
+ m.mapRect(mProtectionRect, mProtectionRectOrig);
}
}
@@ -964,7 +983,8 @@
if (mShowProtection) {
// Make sure that our measured height encompases the protection
mTotalBounds.union(mBoundingRect);
- mTotalBounds.union(mProtectionRect);
+ mTotalBounds.union((int) mProtectionRect.left, (int) mProtectionRect.top,
+ (int) mProtectionRect.right, (int) mProtectionRect.bottom);
setMeasuredDimension(
resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0),
resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0));
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 5077e18..cc4ee89 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -1,6 +1,9 @@
package com.android.systemui.assist;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -43,6 +46,7 @@
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.R;
import com.android.systemui.assist.ui.DefaultUiController;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -51,6 +55,8 @@
import javax.inject.Inject;
import javax.inject.Singleton;
+import dagger.Lazy;
+
/**
* Class to manage everything related to assist in SystemUI.
*/
@@ -98,6 +104,9 @@
public static final String INVOCATION_TYPE_KEY = "invocation_type";
protected static final String ACTION_KEY = "action";
protected static final String SHOW_ASSIST_HANDLES_ACTION = "show_assist_handles";
+ protected static final String SET_ASSIST_GESTURE_CONSTRAINED_ACTION =
+ "set_assist_gesture_constrained";
+ protected static final String CONSTRAINED_KEY = "should_constrain";
public static final int INVOCATION_TYPE_GESTURE = 1;
public static final int INVOCATION_TYPE_ACTIVE_EDGE = 2;
@@ -120,6 +129,7 @@
private final PhoneStateMonitor mPhoneStateMonitor;
private final AssistHandleBehaviorController mHandleController;
private final UiController mUiController;
+ protected final Lazy<SysUiState> mSysUiState;
private AssistOrbContainer mView;
private final DeviceProvisionedController mDeviceProvisionedController;
@@ -185,7 +195,8 @@
CommandQueue commandQueue,
PhoneStateMonitor phoneStateMonitor,
OverviewProxyService overviewProxyService,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ Lazy<SysUiState> sysUiState) {
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
@@ -206,6 +217,8 @@
mUiController = new DefaultUiController(mContext);
+ mSysUiState = sysUiState;
+
overviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
@Override
public void onAssistantProgress(float progress) {
@@ -243,8 +256,16 @@
if (VERBOSE) {
Log.v(TAG, "UI hints received");
}
- if (SHOW_ASSIST_HANDLES_ACTION.equals(hints.getString(ACTION_KEY))) {
+
+ String action = hints.getString(ACTION_KEY);
+ if (SHOW_ASSIST_HANDLES_ACTION.equals(action)) {
requestAssistHandles();
+ } else if (SET_ASSIST_GESTURE_CONSTRAINED_ACTION.equals(action)) {
+ mSysUiState.get()
+ .setFlag(
+ SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
+ hints.getBoolean(CONSTRAINED_KEY, false))
+ .commitUpdate(DEFAULT_DISPLAY);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index cbb1982..0018d33 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -99,6 +99,8 @@
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason;
+ // HAT received from LockSettingsService when credential is verified.
+ @Nullable byte[] mCredentialAttestation;
static class Config {
Context mContext;
@@ -109,6 +111,7 @@
String mOpPackageName;
int mModalityMask;
boolean mSkipIntro;
+ long mOperationId;
}
public static class Builder {
@@ -149,6 +152,11 @@
return this;
}
+ public Builder setOperationId(long operationId) {
+ mConfig.mOperationId = operationId;
+ return this;
+ }
+
public AuthContainerView build(int modalityMask) {
mConfig.mModalityMask = modalityMask;
return new AuthContainerView(mConfig, new Injector());
@@ -224,7 +232,8 @@
final class CredentialCallback implements AuthCredentialView.Callback {
@Override
- public void onCredentialMatched() {
+ public void onCredentialMatched(byte[] attestation) {
+ mCredentialAttestation = attestation;
animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
}
}
@@ -341,6 +350,7 @@
mCredentialView.setContainerView(this);
mCredentialView.setUserId(mConfig.mUserId);
+ mCredentialView.setOperationId(mConfig.mOperationId);
mCredentialView.setEffectiveUserId(mEffectiveUserId);
mCredentialView.setCredentialType(credentialType);
mCredentialView.setCallback(mCredentialCallback);
@@ -558,7 +568,7 @@
private void sendPendingCallbackIfNotNull() {
Log.d(TAG, "pendingCallback: " + mPendingCallbackReason);
if (mPendingCallbackReason != null) {
- mConfig.mCallback.onDismissed(mPendingCallbackReason);
+ mConfig.mCallback.onDismissed(mPendingCallbackReason, mCredentialAttestation);
mPendingCallbackReason = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 6149b0b..c30477c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -20,6 +20,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -99,7 +100,8 @@
try {
if (mReceiver != null) {
- mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
+ null /* credentialAttestation */);
mReceiver = null;
}
} catch (RemoteException e) {
@@ -124,7 +126,8 @@
mCurrentDialog = null;
if (mReceiver != null) {
mReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
+ null /* credentialAttestation */);
mReceiver = null;
}
}
@@ -162,35 +165,42 @@
}
@Override
- public void onDismissed(@DismissedReason int reason) {
+ public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) {
switch (reason) {
case AuthDialogCallback.DISMISSED_USER_CANCELED:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED:
sendResultAndCleanUp(
- BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_ERROR:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED,
+ credentialAttestation);
break;
case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED,
+ credentialAttestation);
break;
default:
@@ -199,13 +209,14 @@
}
}
- private void sendResultAndCleanUp(@DismissedReason int reason) {
+ private void sendResultAndCleanUp(@DismissedReason int reason,
+ @Nullable byte[] credentialAttestation) {
if (mReceiver == null) {
Log.e(TAG, "sendResultAndCleanUp: Receiver is null");
return;
}
try {
- mReceiver.onDialogDismissed(reason);
+ mReceiver.onDialogDismissed(reason, credentialAttestation);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception", e);
}
@@ -251,13 +262,15 @@
@Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
+ long operationId) {
final int authenticators = Utils.getAuthenticators(bundle);
if (DEBUG) {
Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
+ ", biometricModality: " + biometricModality
- + ", requireConfirmation: " + requireConfirmation);
+ + ", requireConfirmation: " + requireConfirmation
+ + ", operationId: " + operationId);
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -266,6 +279,7 @@
args.arg3 = requireConfirmation;
args.argi2 = userId;
args.arg4 = opPackageName;
+ args.arg5 = operationId;
boolean skipAnimation = false;
if (mCurrentDialog != null) {
@@ -354,6 +368,7 @@
final boolean requireConfirmation = (boolean) args.arg3;
final int userId = args.argi2;
final String opPackageName = (String) args.arg4;
+ final long operationId = (long) args.arg5;
// Create a new dialog but do not replace the current one yet.
final AuthDialog newDialog = buildDialog(
@@ -362,7 +377,8 @@
userId,
type,
opPackageName,
- skipAnimation);
+ skipAnimation,
+ operationId);
if (newDialog == null) {
Log.e(TAG, "Unsupported type: " + type);
@@ -429,7 +445,7 @@
}
protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation,
- int userId, int type, String opPackageName, boolean skipIntro) {
+ int userId, int type, String opPackageName, boolean skipIntro, long operationId) {
return new AuthContainerView.Builder(mContext)
.setCallback(this)
.setBiometricPromptBundle(biometricPromptBundle)
@@ -437,6 +453,7 @@
.setUserId(userId)
.setOpPackageName(opPackageName)
.setSkipIntro(skipIntro)
+ .setOperationId(operationId)
.build(type);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 82c8a46..b986f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -103,14 +103,16 @@
return;
}
- mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils,
- password, mEffectiveUserId, this::onCredentialChecked);
+ mPendingLockCheck = LockPatternChecker.verifyCredential(mLockPatternUtils,
+ password, mOperationId, mEffectiveUserId, this::onCredentialVerified);
}
}
@Override
- protected void onCredentialChecked(boolean matched, int timeoutMs) {
- super.onCredentialChecked(matched, timeoutMs);
+ protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
+ super.onCredentialVerified(attestation, timeoutMs);
+
+ final boolean matched = attestation != null;
if (matched) {
mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index 03136a4..6d16f43 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -61,21 +61,22 @@
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
// Pattern size is less than the minimum, do not count it as a failed attempt.
- onPatternChecked(false /* matched */, 0 /* timeoutMs */);
+ onPatternVerified(null /* attestation */, 0 /* timeoutMs */);
return;
}
try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
- mPendingLockCheck = LockPatternChecker.checkCredential(
+ mPendingLockCheck = LockPatternChecker.verifyCredential(
mLockPatternUtils,
credential,
+ mOperationId,
mEffectiveUserId,
- this::onPatternChecked);
+ this::onPatternVerified);
}
}
- private void onPatternChecked(boolean matched, int timeoutMs) {
- AuthCredentialPatternView.this.onCredentialChecked(matched, timeoutMs);
+ private void onPatternVerified(byte[] attestation, int timeoutMs) {
+ AuthCredentialPatternView.this.onCredentialVerified(attestation, timeoutMs);
if (timeoutMs > 0) {
mLockPatternView.setEnabled(false);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 48c6621..0d9d426 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -42,7 +42,7 @@
/**
* Abstract base class for Pin, Pattern, or Password authentication, for
- * {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)}
+ * {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}}
*/
public abstract class AuthCredentialView extends LinearLayout {
@@ -70,11 +70,12 @@
protected Callback mCallback;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected int mUserId;
+ protected long mOperationId;
protected int mEffectiveUserId;
protected ErrorTimer mErrorTimer;
interface Callback {
- void onCredentialMatched();
+ void onCredentialMatched(byte[] attestation);
}
protected static class ErrorTimer extends CountDownTimer {
@@ -148,6 +149,10 @@
mUserId = userId;
}
+ void setOperationId(long operationId) {
+ mOperationId = operationId;
+ }
+
void setEffectiveUserId(int effectiveUserId) {
mEffectiveUserId = effectiveUserId;
}
@@ -245,10 +250,13 @@
protected void onErrorTimeoutFinish() {}
- protected void onCredentialChecked(boolean matched, int timeoutMs) {
+ protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
+
+ final boolean matched = attestation != null;
+
if (matched) {
mClearErrorRunnable.run();
- mCallback.onCredentialMatched();
+ mCallback.onCredentialMatched(attestation);
} else {
if (timeoutMs > 0) {
mHandler.removeCallbacks(mClearErrorRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
index 12bb122..a47621d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics;
import android.annotation.IntDef;
+import android.annotation.Nullable;
/**
* Callback interface for dialog views. These should be implemented by the controller (e.g.
@@ -44,8 +45,9 @@
/**
* Invoked when the dialog is dismissed
* @param reason
+ * @param credentialAttestation the HAT received from LockSettingsService upon verification
*/
- void onDismissed(@DismissedReason int reason);
+ void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation);
/**
* Invoked when the "try again" button is clicked
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index ae1438e..b39dd1a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -52,6 +52,7 @@
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
@@ -83,11 +84,11 @@
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -146,13 +147,15 @@
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
- private int mMaxBubbles;
// Tracks the id of the current (foreground) user.
private int mCurrentUserId;
// Saves notification keys of active bubbles when users are switched.
private final SparseSetArray<String> mSavedBubbleKeysPerUser;
+ // Used when ranking updates occur and we check if things should bubble / unbubble
+ private NotificationListenerService.Ranking mTmpRanking;
+
// Saves notification keys of user created "fake" bubbles so that we can allow notifications
// like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
private final HashSet<String> mUserCreatedBubbles;
@@ -169,7 +172,7 @@
// Callback that updates BubbleOverflowActivity on data change.
@Nullable private Runnable mOverflowCallback = null;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private IStatusBarService mBarService;
// Used for determining view rect for touch interaction
@@ -279,7 +282,7 @@
ShadeController shadeController,
BubbleData data,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
+ NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManager groupManager,
@@ -304,7 +307,7 @@
BubbleData data,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
+ NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManager groupManager,
@@ -316,7 +319,7 @@
dumpManager.registerDumpable(TAG, this);
mContext = context;
mShadeController = shadeController;
- mNotificationInterruptionStateProvider = interruptionStateProvider;
+ mNotificationInterruptStateProvider = interruptionStateProvider;
mNotifUserManager = notifUserManager;
mZenModeController = zenModeController;
mFloatingContentCoordinator = floatingContentCoordinator;
@@ -338,7 +341,6 @@
configurationController.addCallback(this /* configurationListener */);
- mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
@@ -632,7 +634,7 @@
for (NotificationEntry e :
mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
if (savedBubbleKeys.contains(e.getKey())
- && mNotificationInterruptionStateProvider.shouldBubbleUp(e)
+ && mNotificationInterruptStateProvider.shouldBubbleUp(e)
&& canLaunchInActivityView(mContext, e)) {
updateBubble(e, /* suppressFlyout= */ true);
}
@@ -894,7 +896,7 @@
boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
mContext, entry, previouslyUserCreated, userBlocked);
- if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
if (wasAdjusted && !previouslyUserCreated) {
// Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
@@ -910,7 +912,7 @@
boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
mContext, entry, previouslyUserCreated, userBlocked);
- boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
@@ -939,9 +941,29 @@
}
}
+ /**
+ * Called when NotificationListener has received adjusted notification rank and reapplied
+ * filtering and sorting. This is used to dismiss or create bubbles based on changes in
+ * permissions on the notification channel or the global setting.
+ *
+ * @param rankingMap the updated ranking map from NotificationListenerService
+ */
private void onRankingUpdated(RankingMap rankingMap) {
- // Forward to BubbleData to block any bubbles which should no longer be shown
- mBubbleData.notificationRankingUpdated(rankingMap);
+ if (mTmpRanking == null) {
+ mTmpRanking = new NotificationListenerService.Ranking();
+ }
+ String[] orderedKeys = rankingMap.getOrderedKeys();
+ for (int i = 0; i < orderedKeys.length; i++) {
+ String key = orderedKeys[i];
+ NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
+ rankingMap.getRanking(key, mTmpRanking);
+ if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) {
+ mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
+ } else if (entry != null && mTmpRanking.isBubble()) {
+ entry.setFlagBubble(true);
+ onEntryUpdated(entry);
+ }
+ }
}
@SuppressWarnings("FieldCanBeLocal")
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index cf5a4d3..ff5e13c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -26,7 +26,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.RankingMap;
import android.util.Log;
import android.util.Pair;
@@ -207,12 +206,13 @@
// Preserve new order for next repack, which sorts by last updated time.
bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
- setSelectedBubbleInternal(bubble);
mOverflowBubbles.remove(bubble);
-
bubble.inflate(
- b -> notificationEntryUpdated(bubble, /* suppressFlyout */
- false, /* showInShade */ true),
+ b -> {
+ notificationEntryUpdated(bubble, /* suppressFlyout */
+ false, /* showInShade */ true);
+ setSelectedBubbleInternal(bubble);
+ },
mContext, stack, factory);
dispatchPendingChanges();
}
@@ -225,6 +225,14 @@
Bubble getOrCreateBubble(NotificationEntry entry) {
Bubble bubble = getBubbleWithKey(entry.getKey());
if (bubble == null) {
+ for (int i = 0; i < mOverflowBubbles.size(); i++) {
+ Bubble b = mOverflowBubbles.get(i);
+ if (b.getKey().equals(entry.getKey())) {
+ mOverflowBubbles.remove(b);
+ mPendingBubbles.add(b);
+ return b;
+ }
+ }
// Check for it in pending
for (int i = 0; i < mPendingBubbles.size(); i++) {
Bubble b = mPendingBubbles.get(i);
@@ -289,31 +297,6 @@
}
/**
- * Called when NotificationListener has received adjusted notification rank and reapplied
- * filtering and sorting. This is used to dismiss any bubbles which should no longer be shown
- * due to changes in permissions on the notification channel or the global setting.
- *
- * @param rankingMap the updated ranking map from NotificationListenerService
- */
- public void notificationRankingUpdated(RankingMap rankingMap) {
- if (mTmpRanking == null) {
- mTmpRanking = new NotificationListenerService.Ranking();
- }
-
- String[] orderedKeys = rankingMap.getOrderedKeys();
- for (int i = 0; i < orderedKeys.length; i++) {
- String key = orderedKeys[i];
- if (hasBubbleWithKey(key)) {
- rankingMap.getRanking(key, mTmpRanking);
- if (!mTmpRanking.canBubble()) {
- doRemove(key, BubbleController.DISMISS_BLOCKED);
- }
- }
- }
- dispatchPendingChanges();
- }
-
- /**
* Adds a group key indicating that the summary for this group should be suppressed.
*
* @param groupKey the group key of the group whose summary should be suppressed.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index aedd2db..e666fb5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -424,6 +424,7 @@
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+ @Nullable
private BubbleOverflow mBubbleOverflow;
private boolean mShouldShowUserEducation;
@@ -470,7 +471,8 @@
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
- mStackAnimationController = new StackAnimationController(floatingContentCoordinator);
+ mStackAnimationController = new StackAnimationController(
+ floatingContentCoordinator, this::getBubbleCount);
mExpandedAnimationController = new ExpandedAnimationController(
mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation);
@@ -1386,6 +1388,11 @@
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "onBubbleDragStart: bubble=" + bubble);
}
+
+ if (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView())) {
+ return;
+ }
+
mExpandedAnimationController.prepareForBubbleDrag(bubble, mMagneticTarget);
// We're dragging an individual bubble, so set the magnetized object to the magnetized
@@ -1398,7 +1405,8 @@
/** Called with the coordinates to which an individual bubble has been dragged. */
public void onBubbleDragged(View bubble, float x, float y) {
- if (!mIsExpanded || mIsExpansionAnimating) {
+ if (!mIsExpanded || mIsExpansionAnimating
+ || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) {
return;
}
@@ -1413,7 +1421,8 @@
Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble);
}
- if (!mIsExpanded || mIsExpansionAnimating) {
+ if (!mIsExpanded || mIsExpansionAnimating
+ || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) {
return;
}
@@ -1421,6 +1430,18 @@
hideDismissTarget();
}
+ /** Expands the clicked bubble. */
+ public void expandBubble(Bubble bubble) {
+ if (bubble.equals(mBubbleData.getSelectedBubble())) {
+ // If the bubble we're supposed to expand is the selected bubble, that means the
+ // overflow bubble is currently expanded. Don't tell BubbleData to set this bubble as
+ // selected, since it already is. Just call the stack's setSelectedBubble to expand it.
+ setSelectedBubble(bubble);
+ } else {
+ mBubbleData.setSelectedBubble(bubble);
+ }
+ }
+
void onDragStart() {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "onDragStart()");
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 0c5bef4..132c45f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -189,7 +189,7 @@
if (key == BubbleOverflow.KEY) {
mStack.showOverflow();
} else {
- mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key));
+ mStack.expandBubble(mBubbleData.getBubbleWithKey(key));
}
}
resetForNextGesture();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 3eaa90c..9b5dc31 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -409,11 +409,8 @@
mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
// Includes overflow button.
- // TODO(b/148675523) this is a temporary work around; change back once we have proper fix.
-// float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
-// - (mBubblesMaxRendered + 1) * mBubbleSizePx;
- float totalGapWidth = getAvailableScreenWidth(true /* includeStableInsets */)
- - (mExpandedViewPadding * 2) - (mBubblesMaxRendered + 1) * mBubbleSizePx;
+ float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
+ - (mBubblesMaxRendered + 1) * mBubbleSizePx;
mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
// Ensure that all child views are at 1x scale, and visible, in case they were animating
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 86387f1..7ee162e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -43,6 +43,7 @@
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Set;
+import java.util.function.IntSupplier;
/**
* Animation controller for bubbles when they're in their stacked state. Stacked bubbles sit atop
@@ -88,6 +89,9 @@
private static final int SPRING_AFTER_FLING_STIFFNESS = 750;
private static final float SPRING_AFTER_FLING_DAMPING_RATIO = 0.85f;
+ /** Sentinel value for unset position value. */
+ private static final float UNSET = -Float.MIN_VALUE;
+
/**
* Minimum fling velocity required to trigger moving the stack from one side of the screen to
* the other.
@@ -132,7 +136,7 @@
* The Y position of the stack before the IME became visible, or {@link Float#MIN_VALUE} if the
* IME is not visible or the user moved the stack since the IME became visible.
*/
- private float mPreImeY = Float.MIN_VALUE;
+ private float mPreImeY = UNSET;
/**
* Animations on the stack position itself, which would have been started in
@@ -241,9 +245,15 @@
}
};
+ /** Returns the number of 'real' bubbles (excluding the overflow bubble). */
+ private IntSupplier mBubbleCountSupplier;
+
public StackAnimationController(
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ IntSupplier bubbleCountSupplier) {
mFloatingContentCoordinator = floatingContentCoordinator;
+ mBubbleCountSupplier = bubbleCountSupplier;
+
}
/**
@@ -256,7 +266,7 @@
// If we manually move the bubbles with the IME open, clear the return point since we don't
// want the stack to snap away from the new position.
- mPreImeY = Float.MIN_VALUE;
+ mPreImeY = UNSET;
moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_X, x);
moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_Y, y);
@@ -505,26 +515,27 @@
* Animates the stack either away from the newly visible IME, or back to its original position
* due to the IME going away.
*
- * @return The destination Y value of the stack due to the IME movement.
+ * @return The destination Y value of the stack due to the IME movement (or the current position
+ * of the stack if it's not moving).
*/
public float animateForImeVisibility(boolean imeVisible) {
final float maxBubbleY = getAllowableStackPositionRegion().bottom;
- float destinationY = Float.MIN_VALUE;
+ float destinationY = UNSET;
if (imeVisible) {
// Stack is lower than it should be and overlaps the now-visible IME.
- if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) {
+ if (mStackPosition.y > maxBubbleY && mPreImeY == UNSET) {
mPreImeY = mStackPosition.y;
destinationY = maxBubbleY;
}
} else {
- if (mPreImeY > Float.MIN_VALUE) {
+ if (mPreImeY != UNSET) {
destinationY = mPreImeY;
- mPreImeY = Float.MIN_VALUE;
+ mPreImeY = UNSET;
}
}
- if (destinationY > Float.MIN_VALUE) {
+ if (destinationY != UNSET) {
springFirstBubbleWithStackFollowing(
DynamicAnimation.TRANSLATION_Y,
getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null)
@@ -535,7 +546,7 @@
notifyFloatingCoordinatorStackAnimatingTo(mStackPosition.x, destinationY);
}
- return destinationY;
+ return destinationY != UNSET ? destinationY : mStackPosition.y;
}
/**
@@ -588,7 +599,7 @@
mLayout.getHeight()
- mBubbleSize
- mBubblePaddingTop
- - (mImeHeight > Float.MIN_VALUE ? mImeHeight + mBubblePaddingTop : 0f)
+ - (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f)
- Math.max(
insets.getStableInsetBottom(),
insets.getDisplayCutout() != null
@@ -669,6 +680,8 @@
new SpringAnimation(this, firstBubbleProperty)
.setSpring(spring)
.addEndListener((dynamicAnimation, b, v, v1) -> {
+ mRestingStackPosition.set(mStackPosition);
+
if (after != null) {
for (Runnable callback : after) {
callback.run();
@@ -736,7 +749,7 @@
return;
}
- if (mLayout.getChildCount() == 1) {
+ if (getBubbleCount() == 1) {
// If this is the first child added, position the stack in its starting position.
moveStackToStartPosition();
} else if (isStackPositionSet() && mLayout.indexOfChild(child) == 0) {
@@ -758,7 +771,7 @@
.start();
// If there are other bubbles, pull them into the correct position.
- if (mLayout.getChildCount() > 0) {
+ if (getBubbleCount() > 0) {
animationForChildAtIndex(0).translationX(mStackPosition.x).start();
} else {
// When all children are removed ensure stack position is sane
@@ -979,6 +992,11 @@
return mMagnetizedStack;
}
+ /** Returns the number of 'real' bubbles (excluding overflow). */
+ private int getBubbleCount() {
+ return mBubbleCountSupplier.getAsInt();
+ }
+
/**
* FloatProperty that uses {@link #moveFirstBubbleWithStackFollowing} to set the first bubble's
* translation and animate the rest of the stack with it. A DynamicAnimation can animate this
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index ac97d8a..27c9e98 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -25,8 +25,8 @@
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -54,7 +54,7 @@
ShadeController shadeController,
BubbleData data,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
+ NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index f3bd0b5..f8d4a39 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -31,7 +31,6 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.DelayableExecutor
import dagger.Lazy
-import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import javax.inject.Singleton
@@ -47,8 +46,6 @@
private const val TAG = "ControlsBindingControllerImpl"
}
- private val refreshing = AtomicBoolean(false)
-
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
override val currentUserId: Int
@@ -56,6 +53,12 @@
private var currentProvider: ControlsProviderLifecycleManager? = null
+ /*
+ * Will track any active subscriber for subscribe/unsubscribe requests coming into
+ * this controller. Only one can be active at any time
+ */
+ private var statefulControlSubscriber: StatefulControlSubscriber? = null
+
private val actionCallbackService = object : IControlsActionCallback.Stub() {
override fun accept(
token: IBinder,
@@ -66,27 +69,6 @@
}
}
- private val subscriberService = object : IControlsSubscriber.Stub() {
- override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
- backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
- }
-
- override fun onNext(token: IBinder, c: Control) {
- if (!refreshing.get()) {
- Log.d(TAG, "Refresh outside of window for token:$token")
- } else {
- backgroundExecutor.execute(OnNextRunnable(token, c))
- }
- }
- override fun onError(token: IBinder, s: String) {
- backgroundExecutor.execute(OnErrorRunnable(token, s))
- }
-
- override fun onComplete(token: IBinder) {
- backgroundExecutor.execute(OnCompleteRunnable(token))
- }
- }
-
@VisibleForTesting
internal open fun createProviderManager(component: ComponentName):
ControlsProviderLifecycleManager {
@@ -94,23 +76,21 @@
context,
backgroundExecutor,
actionCallbackService,
- subscriberService,
currentUser,
component
)
}
private fun retrieveLifecycleManager(component: ComponentName):
- ControlsProviderLifecycleManager? {
+ ControlsProviderLifecycleManager {
if (currentProvider != null && currentProvider?.componentName != component) {
unbind()
}
- if (currentProvider == null) {
- currentProvider = createProviderManager(component)
- }
+ val provider = currentProvider ?: createProviderManager(component)
+ currentProvider = provider
- return currentProvider
+ return provider
}
override fun bindAndLoad(
@@ -118,21 +98,23 @@
callback: ControlsBindingController.LoadCallback
): Runnable {
val subscriber = LoadSubscriber(callback)
- retrieveLifecycleManager(component)?.maybeBindAndLoad(subscriber)
+ retrieveLifecycleManager(component).maybeBindAndLoad(subscriber)
return subscriber.loadCancel()
}
override fun subscribe(structureInfo: StructureInfo) {
- if (refreshing.compareAndSet(false, true)) {
- val provider = retrieveLifecycleManager(structureInfo.componentName)
- provider?.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId })
- }
+ // make sure this has happened. only allow one active subscription
+ unsubscribe()
+
+ statefulControlSubscriber = null
+ val provider = retrieveLifecycleManager(structureInfo.componentName)
+ val scs = StatefulControlSubscriber(lazyController.get(), provider, backgroundExecutor)
+ statefulControlSubscriber = scs
+ provider.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }, scs)
}
override fun unsubscribe() {
- if (refreshing.compareAndSet(true, false)) {
- currentProvider?.unsubscribe()
- }
+ statefulControlSubscriber?.cancel()
}
override fun action(
@@ -140,20 +122,24 @@
controlInfo: ControlInfo,
action: ControlAction
) {
- retrieveLifecycleManager(componentName)
- ?.maybeBindAndSendAction(controlInfo.controlId, action)
+ if (statefulControlSubscriber == null) {
+ Log.w(TAG, "No actions can occur outside of an active subscription. Ignoring.")
+ } else {
+ retrieveLifecycleManager(componentName)
+ .maybeBindAndSendAction(controlInfo.controlId, action)
+ }
}
override fun bindService(component: ComponentName) {
- retrieveLifecycleManager(component)?.bindService()
+ retrieveLifecycleManager(component).bindService()
}
override fun changeUser(newUser: UserHandle) {
if (newUser == currentUser) return
+ unsubscribe()
unbind()
-
- refreshing.set(false)
+ currentProvider = null
currentUser = newUser
}
@@ -174,8 +160,8 @@
override fun toString(): String {
return StringBuilder(" ControlsBindingController:\n").apply {
- append(" refreshing=${refreshing.get()}\n")
append(" currentUser=$currentUser\n")
+ append(" StatefulControlSubscriber=$statefulControlSubscriber")
append(" Providers=$currentProvider\n")
}.toString()
}
@@ -213,53 +199,12 @@
}
}
- private inner class OnNextRunnable(
- token: IBinder,
- val control: Control
- ) : CallbackRunnable(token) {
- override fun doRun() {
- if (!refreshing.get()) {
- Log.d(TAG, "onRefresh outside of window from:${provider?.componentName}")
- }
-
- provider?.let {
- lazyController.get().refreshStatus(it.componentName, control)
- }
- }
- }
-
private inner class OnSubscribeRunnable(
token: IBinder,
val subscription: IControlsSubscription
) : CallbackRunnable(token) {
override fun doRun() {
- if (!refreshing.get()) {
- Log.d(TAG, "onRefresh outside of window from '${provider?.componentName}'")
- }
- provider?.let {
- it.startSubscription(subscription)
- }
- }
- }
-
- private inner class OnCompleteRunnable(
- token: IBinder
- ) : CallbackRunnable(token) {
- override fun doRun() {
- provider?.let {
- Log.i(TAG, "onComplete receive from '${it.componentName}'")
- }
- }
- }
-
- private inner class OnErrorRunnable(
- token: IBinder,
- val error: String
- ) : CallbackRunnable(token) {
- override fun doRun() {
- provider?.let {
- Log.e(TAG, "onError receive from '${it.componentName}': $error")
- }
+ provider?.startSubscription(subscription)
}
}
@@ -310,12 +255,14 @@
override fun onError(token: IBinder, s: String) {
hasError = true
_loadCancelInternal = {}
+ currentProvider?.cancelLoadTimeout()
backgroundExecutor.execute(OnLoadErrorRunnable(token, s, callback))
}
override fun onComplete(token: IBinder) {
_loadCancelInternal = {}
if (!hasError) {
+ currentProvider?.cancelLoadTimeout()
backgroundExecutor.execute(OnLoadRunnable(token, loadedControls, callback))
}
}
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 86e8e83..4918bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -58,7 +58,6 @@
private val context: Context,
private val executor: DelayableExecutor,
private val actionCallbackService: IControlsActionCallback.Stub,
- private val subscriberService: IControlsSubscriber.Stub,
val user: UserHandle,
val componentName: ComponentName
) : IBinder.DeathRecipient {
@@ -157,10 +156,9 @@
load(msg.subscriber)
}
- queue.filter { it is Message.Subscribe }.flatMap { (it as Message.Subscribe).list }.run {
- if (this.isNotEmpty()) {
- subscribe(this)
- }
+ queue.filter { it is Message.Subscribe }.forEach {
+ val msg = it as Message.Subscribe
+ subscribe(msg.list, msg.subscriber)
}
queue.filter { it is Message.Action }.forEach {
val msg = it as Message.Action
@@ -185,9 +183,9 @@
}
}
- private fun unqueueMessage(message: Message) {
+ private fun unqueueMessageType(type: Int) {
synchronized(queuedMessages) {
- queuedMessages.removeIf { it.type == message.type }
+ queuedMessages.removeIf { it.type == type }
}
}
@@ -219,7 +217,7 @@
* @param subscriber the subscriber that manages coordination for loading controls
*/
fun maybeBindAndLoad(subscriber: IControlsSubscriber.Stub) {
- unqueueMessage(Message.Unbind)
+ unqueueMessageType(MSG_UNBIND)
onLoadCanceller = executor.executeDelayed({
// Didn't receive a response in time, log and send back error
Log.d(TAG, "Timeout waiting onLoad for $componentName")
@@ -230,6 +228,11 @@
invokeOrQueue({ load(subscriber) }, Message.Load(subscriber))
}
+ fun cancelLoadTimeout() {
+ onLoadCanceller?.run()
+ onLoadCanceller = null
+ }
+
/**
* Request a subscription to the [Publisher] returned by [ControlsProviderService.publisherFor]
*
@@ -237,16 +240,20 @@
*
* @param controlIds a list of the ids of controls to send status back.
*/
- fun maybeBindAndSubscribe(controlIds: List<String>) {
- invokeOrQueue({ subscribe(controlIds) }, Message.Subscribe(controlIds))
+ fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
+ invokeOrQueue(
+ { subscribe(controlIds, subscriber) },
+ Message.Subscribe(controlIds, subscriber)
+ )
}
- private fun subscribe(controlIds: List<String>) {
+ private fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
if (DEBUG) {
Log.d(TAG, "subscribe $componentName - $controlIds")
}
- if (!(wrapper?.subscribe(controlIds, subscriberService) ?: false)) {
- queueMessage(Message.Subscribe(controlIds))
+
+ if (!(wrapper?.subscribe(controlIds, subscriber) ?: false)) {
+ queueMessage(Message.Subscribe(controlIds, subscriber))
binderDied()
}
}
@@ -276,10 +283,13 @@
/**
* Starts the subscription to the [ControlsProviderService] and requests status of controls.
*
- * @param subscription the subscriber to use to request controls
+ * @param subscription the subscription to use to request controls
* @see maybeBindAndLoad
*/
fun startSubscription(subscription: IControlsSubscription) {
+ if (DEBUG) {
+ Log.d(TAG, "startSubscription: $subscription")
+ }
synchronized(subscriptions) {
subscriptions.add(subscription)
}
@@ -287,30 +297,26 @@
}
/**
- * Unsubscribe from this service, cancelling all status requests.
+ * Cancels the subscription to the [ControlsProviderService].
+ *
+ * @param subscription the subscription to cancel
+ * @see maybeBindAndLoad
*/
- fun unsubscribe() {
+ fun cancelSubscription(subscription: IControlsSubscription) {
if (DEBUG) {
- Log.d(TAG, "unsubscribe $componentName")
+ Log.d(TAG, "cancelSubscription: $subscription")
}
- unqueueMessage(Message.Subscribe(emptyList())) // Removes all subscribe messages
-
- val subs = synchronized(subscriptions) {
- ArrayList(subscriptions).also {
- subscriptions.clear()
- }
+ synchronized(subscriptions) {
+ subscriptions.remove(subscription)
}
-
- subs.forEach {
- wrapper?.cancel(it)
- }
+ wrapper?.cancel(subscription)
}
/**
* Request bind to the service.
*/
fun bindService() {
- unqueueMessage(Message.Unbind)
+ unqueueMessageType(MSG_UNBIND)
bindService(true)
}
@@ -321,8 +327,16 @@
onLoadCanceller?.run()
onLoadCanceller = null
- // just in case this wasn't called already
- unsubscribe()
+ // be sure to cancel all subscriptions
+ val subs = synchronized(subscriptions) {
+ ArrayList(subscriptions).also {
+ subscriptions.clear()
+ }
+ }
+
+ subs.forEach {
+ wrapper?.cancel(it)
+ }
bindService(false)
}
@@ -346,7 +360,7 @@
object Unbind : Message() {
override val type = MSG_UNBIND
}
- class Subscribe(val list: List<String>) : Message() {
+ class Subscribe(val list: List<String>, val subscriber: IControlsSubscriber) : Message() {
override val type = MSG_SUBSCRIBE
}
class Action(val id: String, val action: ControlAction) : Message() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
new file mode 100644
index 0000000..a371aa6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.controller
+
+import android.os.IBinder
+import android.service.controls.Control
+import android.service.controls.IControlsSubscriber
+import android.service.controls.IControlsSubscription
+import android.util.Log
+import com.android.systemui.util.concurrency.DelayableExecutor
+
+/**
+ * A single subscriber, supporting stateful controls for publishers created by
+ * {@link ControlsProviderService#createPublisherFor}. In general, this subscription will remain
+ * active until the SysUi chooses to cancel it.
+ */
+class StatefulControlSubscriber(
+ private val controller: ControlsController,
+ private val provider: ControlsProviderLifecycleManager,
+ private val bgExecutor: DelayableExecutor
+) : IControlsSubscriber.Stub() {
+ private var subscriptionOpen = false
+ private var subscription: IControlsSubscription? = null
+
+ companion object {
+ private const val TAG = "StatefulControlSubscriber"
+ }
+
+ private fun run(token: IBinder, f: () -> Unit) {
+ if (provider.token == token) {
+ bgExecutor.execute { f() }
+ }
+ }
+
+ override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
+ run(token) {
+ subscriptionOpen = true
+ subscription = subs
+ provider.startSubscription(subs)
+ }
+ }
+
+ override fun onNext(token: IBinder, control: Control) {
+ run(token) {
+ if (!subscriptionOpen) {
+ Log.w(TAG, "Refresh outside of window for token:$token")
+ } else {
+ controller.refreshStatus(provider.componentName, control)
+ }
+ }
+ }
+ override fun onError(token: IBinder, error: String) {
+ run(token) {
+ if (subscriptionOpen) {
+ subscriptionOpen = false
+ Log.e(TAG, "onError receive from '${provider.componentName}': $error")
+ }
+ }
+ }
+
+ override fun onComplete(token: IBinder) {
+ run(token) {
+ if (subscriptionOpen) {
+ subscriptionOpen = false
+ Log.i(TAG, "onComplete receive from '${provider.componentName}'")
+ }
+ }
+ }
+
+ fun cancel() {
+ if (!subscriptionOpen) return
+ bgExecutor.execute {
+ if (subscriptionOpen) {
+ subscriptionOpen = false
+ subscription?.let {
+ provider.cancelSubscription(it)
+ }
+ subscription = null
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index fc5663f..b439206 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -17,8 +17,8 @@
package com.android.systemui.controls.ui
import android.content.Context
-import android.graphics.BlendMode
import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
import android.service.controls.Control
import android.service.controls.actions.ControlAction
@@ -39,6 +39,8 @@
import kotlin.reflect.KClass
private const val UPDATE_DELAY_IN_MILLIS = 3000L
+private const val ALPHA_ENABLED = (255.0 * 0.2).toInt()
+private const val ALPHA_DISABLED = 255
class ControlViewHolder(
val layout: ViewGroup,
@@ -69,7 +71,7 @@
cancelUpdate?.run()
- val (status, template) = cws.control?.let {
+ val (controlStatus, template) = cws.control?.let {
title.setText(it.getTitle())
subtitle.setText(it.getSubtitle())
Pair(it.getStatus(), it.getControlTemplate())
@@ -80,20 +82,28 @@
}
cws.control?.let {
+ layout.setClickable(true)
layout.setOnLongClickListener(View.OnLongClickListener() {
ControlActionCoordinator.longPress(this@ControlViewHolder)
true
})
}
- val clazz = findBehavior(status, template)
+ val clazz = findBehavior(controlStatus, template)
if (behavior == null || behavior!!::class != clazz) {
// Behavior changes can signal a change in template from the app or
// first time setup
behavior = clazz.java.newInstance()
behavior?.initialize(this)
+
+ // let behaviors define their own, if necessary, and clear any existing ones
+ layout.setAccessibilityDelegate(null)
}
+
behavior?.bind(cws)
+
+ layout.setContentDescription(
+ "${title.text} ${subtitle.text} ${status.text} ${statusExtra.text}")
}
fun actionResponse(@ControlAction.ResponseResult response: Int) {
@@ -136,16 +146,21 @@
val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset)
val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
- val bg = context.getResources().getColorStateList(ri.background, context.getTheme())
+ val (bg, alpha) = if (enabled) {
+ Pair(ri.enabledBackground, ALPHA_ENABLED)
+ } else {
+ Pair(R.color.control_default_background, ALPHA_DISABLED)
+ }
+
status.setTextColor(fg)
statusExtra.setTextColor(fg)
icon.setImageDrawable(ri.icon)
icon.setImageTintList(fg)
- clipLayer.getDrawable().apply {
- setTintBlendMode(BlendMode.HUE)
- setTintList(bg)
+ (clipLayer.getDrawable() as GradientDrawable).apply {
+ setColor(context.getResources().getColor(bg, context.getTheme()))
+ setAlpha(alpha)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index bde966c..138cd47 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -406,7 +406,7 @@
setText(item.getTitle())
}
view.requireViewById<ImageView>(R.id.app_icon).apply {
- setContentDescription(item.getTitle())
+ setContentDescription(item.appName)
setImageDrawable(item.icon)
}
return view
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index 56267be..27e4649 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.ui
+import android.annotation.ColorRes
import android.annotation.MainThread
import android.content.ComponentName
import android.content.Context
@@ -37,8 +38,11 @@
}
}
-data class RenderInfo(val icon: Drawable, val foreground: Int, val background: Int) {
-
+data class RenderInfo(
+ val icon: Drawable,
+ val foreground: Int,
+ @ColorRes val enabledBackground: Int
+) {
companion object {
const val APP_ICON_ID = -1
private val iconMap = SparseArray<Drawable>()
@@ -72,6 +76,7 @@
icon = iconMap.get(resourceId)
if (icon == null) {
icon = context.resources.getDrawable(resourceId, null)
+ icon.mutate()
iconMap.put(resourceId, icon)
}
}
@@ -94,12 +99,13 @@
private val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
(THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to
- Pair(R.color.thermo_heat_foreground, R.color.thermo_heat_background),
+ Pair(R.color.thermo_heat_foreground, R.color.control_enabled_thermo_heat_background),
(THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to
- Pair(R.color.thermo_cool_foreground, R.color.thermo_cool_background),
- DeviceTypes.TYPE_LIGHT to Pair(R.color.light_foreground, R.color.light_background)
+ Pair(R.color.thermo_cool_foreground, R.color.control_enabled_thermo_cool_background),
+ DeviceTypes.TYPE_LIGHT
+ to Pair(R.color.light_foreground, R.color.control_enabled_light_background)
).withDefault {
- Pair(R.color.control_foreground, R.color.control_background)
+ Pair(R.color.control_foreground, R.color.control_enabled_default_background)
}
private val deviceIconMap = mapOf<Int, IconState>(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index 6595b55..c495c58 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.ui
+import android.os.Bundle
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
@@ -24,6 +25,9 @@
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
import android.service.controls.Control
import android.service.controls.actions.FloatAction
@@ -94,6 +98,71 @@
updateRange(currentRatio, checked)
cvh.applyRenderInfo(checked)
+
+ /*
+ * This is custom widget behavior, so add a new accessibility delegate to
+ * handle clicks and range events. Present as a seek bar control.
+ */
+ cvh.layout.setAccessibilityDelegate(object : View.AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(
+ host: View,
+ info: AccessibilityNodeInfo
+ ) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+
+ val min = levelToRangeValue(MIN_LEVEL)
+ val current = levelToRangeValue(clipLayer.getLevel())
+ val max = levelToRangeValue(MAX_LEVEL)
+
+ val step = rangeTemplate.getStepValue().toDouble()
+ val type = if (step == Math.floor(step)) {
+ AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT
+ } else {
+ AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_FLOAT
+ }
+
+ val rangeInfo = AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current)
+ info.setRangeInfo(rangeInfo)
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS)
+ }
+
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ arguments: Bundle?
+ ): Boolean {
+ val handled = when (action) {
+ AccessibilityNodeInfo.ACTION_CLICK -> {
+ ControlActionCoordinator.toggle(cvh, template.getTemplateId(),
+ template.isChecked())
+ true
+ }
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS.getId() -> {
+ if (arguments == null || !arguments.containsKey(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)) {
+ false
+ } else {
+ val value = arguments.getFloat(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)
+ val ratioDiff = (value - rangeTemplate.getCurrentValue()) /
+ (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
+ updateRange(ratioDiff, template.isChecked())
+ endUpdateRange()
+ true
+ }
+ }
+ else -> false
+ }
+
+ return handled || super.performAccessibilityAction(host, action, arguments)
+ }
+
+ override fun onRequestSendAccessibilityEvent(
+ host: ViewGroup,
+ child: View,
+ event: AccessibilityEvent
+ ): Boolean = false
+ })
}
fun beginUpdateRange() {
@@ -108,7 +177,7 @@
clipLayer.setLevel(newLevel)
if (checked) {
- val newValue = levelToRangeValue()
+ val newValue = levelToRangeValue(clipLayer.getLevel())
val formattedNewValue = format(rangeTemplate.getFormatString().toString(),
DEFAULT_FORMAT, newValue)
@@ -133,8 +202,8 @@
}
}
- private fun levelToRangeValue(): Float {
- val ratio = clipLayer.getLevel().toFloat() / MAX_LEVEL
+ private fun levelToRangeValue(i: Int): Float {
+ val ratio = i.toFloat() / MAX_LEVEL
return rangeTemplate.getMinValue() +
(ratio * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
}
@@ -143,7 +212,8 @@
statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
.getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
status.setVisibility(View.VISIBLE)
- cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(levelToRangeValue())))
+ cvh.action(FloatAction(rangeTemplate.getTemplateId(),
+ findNearestStep(levelToRangeValue(clipLayer.getLevel()))))
}
fun findNearestStep(value: Float): Float {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 6c502d2..3a4b273 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -90,7 +90,7 @@
/** */
@Provides
- public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) {
+ public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) {
return new AmbientDisplayConfiguration(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 956b4aa..8c572fe 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -26,9 +26,11 @@
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
import com.android.systemui.stackdivider.DividerModule;
@@ -85,6 +87,10 @@
BatteryControllerImpl controllerImpl);
@Binds
+ @Singleton
+ public abstract QSFactory provideQSFactory(QSFactoryImpl qsFactoryImpl);
+
+ @Binds
abstract DockManager bindDockManager(DockManagerImpl dockManager);
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 513580f..2e9ce12 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.view.Choreographer;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BootCompleteCache;
@@ -31,22 +30,16 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
-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.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.time.SystemClock;
@@ -98,21 +91,6 @@
keyguardUpdateMonitor, dumpManager);
}
- @Singleton
- @Provides
- @Nullable
- static NotificationShadeWindowBlurController providesBlurController(BlurUtils blurUtils,
- SysuiStatusBarStateController statusBarStateController,
- DumpManager dumpManager, BiometricUnlockController biometricUnlockController,
- KeyguardStateController keyguardStateController,
- NotificationShadeWindowController notificationShadeWindowController,
- Choreographer choreographer) {
- return blurUtils.supportsBlursOnWindows() ? new NotificationShadeWindowBlurController(
- statusBarStateController, blurUtils, biometricUnlockController,
- keyguardStateController, notificationShadeWindowController, choreographer,
- dumpManager) : null;
- }
-
/** */
@Binds
public abstract NotificationRowBinder bindNotificationRowBinder(
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index c45063a..f6fccc0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -161,15 +161,8 @@
}
int scrimOpacity = -1;
- if (mPaused || mScreenOff) {
- // If AOD is paused, force the screen black until the
- // sensor reports a new brightness. This ensures that when the screen comes on
- // again, it will only show after the brightness sensor has stabilized,
- // avoiding a potential flicker.
- scrimOpacity = 255;
- } else if (!mScreenOff && mLightSensor == null) {
- // No light sensor but previous state turned the screen black. Make the scrim
- // transparent and below views visible.
+ if (mLightSensor == null) {
+ // No light sensor, scrims are always transparent.
scrimOpacity = 0;
} else if (brightnessReady) {
// Only unblank scrim once brightness is ready.
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 786ad2c..b99d765 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -100,6 +100,7 @@
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -115,16 +116,17 @@
import javax.inject.Inject;
/**
- * Helper to show the global actions dialog. Each item is an {@link Action} that
- * may show depending on whether the keyguard is showing, and whether the device
- * is provisioned.
+ * Helper to show the global actions dialog. Each item is an {@link Action} that may show depending
+ * on whether the keyguard is showing, and whether the device is provisioned.
*/
public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
- DialogInterface.OnShowListener, ConfigurationController.ConfigurationListener {
+ DialogInterface.OnShowListener,
+ ConfigurationController.ConfigurationListener,
+ GlobalActionsPanelPlugin.Callbacks {
- static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
- static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
- static public final String SYSTEM_DIALOG_REASON_DREAM = "dream";
+ public static final String SYSTEM_DIALOG_REASON_KEY = "reason";
+ public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
+ public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
private static final String TAG = "GlobalActionsDialog";
@@ -162,6 +164,7 @@
private final IActivityManager mIActivityManager;
private final TelecomManager mTelecomManager;
private final MetricsLogger mMetricsLogger;
+ private final NotificationShadeDepthController mDepthController;
private final BlurUtils mBlurUtils;
private ArrayList<Action> mItems;
@@ -207,8 +210,8 @@
KeyguardStateController keyguardStateController, UserManager userManager,
TrustManager trustManager, IActivityManager iActivityManager,
@Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
- BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
- IStatusBarService statusBarService,
+ NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor,
+ IStatusBarService statusBarService, BlurUtils blurUtils,
NotificationShadeWindowController notificationShadeWindowController,
ControlsUiController controlsUiController, IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
@@ -229,7 +232,7 @@
mIActivityManager = iActivityManager;
mTelecomManager = telecomManager;
mMetricsLogger = metricsLogger;
- mBlurUtils = blurUtils;
+ mDepthController = depthController;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -237,6 +240,7 @@
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
mControlsListingController = controlsListingController;
+ mBlurUtils = blurUtils;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -268,8 +272,9 @@
@Override
public void onUnlockedChanged() {
if (mDialog != null && mDialog.mPanelController != null) {
- boolean locked = !keyguardStateController.canDismissLockScreen();
- mDialog.mPanelController.onDeviceLockStateChanged(locked);
+ boolean unlocked = keyguardStateController.isUnlocked()
+ || keyguardStateController.canDismissLockScreen();
+ mDialog.mPanelController.onDeviceLockStateChanged(unlocked);
}
}
});
@@ -383,7 +388,7 @@
mItems.add(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
if (Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
+ Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
&& shouldDisplayLockdown()) {
mItems.add(getLockdownAction());
}
@@ -417,29 +422,10 @@
mAdapter = new MyAdapter();
- GlobalActionsPanelPlugin.PanelViewController panelViewController =
- mPanelPlugin != null
- ? mPanelPlugin.onPanelShown(
- new GlobalActionsPanelPlugin.Callbacks() {
- @Override
- public void dismissGlobalActionsMenu() {
- dismissDialog();
- }
-
- @Override
- public void startPendingIntentDismissingKeyguard(
- PendingIntent intent) {
- mActivityStarter
- .startPendingIntentDismissingKeyguard(intent);
- }
- },
- !mKeyguardStateController.isUnlocked())
- : null;
-
- ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
- mBlurUtils, mSysuiColorExtractor, mStatusBarService,
+ ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, getWalletPanelViewController(),
+ mDepthController, mSysuiColorExtractor, mStatusBarService,
mNotificationShadeWindowController,
- shouldShowControls() ? mControlsUiController : null);
+ shouldShowControls() ? mControlsUiController : null, mBlurUtils);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
@@ -474,6 +460,33 @@
mConfigurationController.removeCallback(this);
}
+ @Nullable
+ private GlobalActionsPanelPlugin.PanelViewController getWalletPanelViewController() {
+ if (mPanelPlugin == null) {
+ return null;
+ }
+ return mPanelPlugin.onPanelShown(this, !mKeyguardStateController.isUnlocked());
+ }
+
+ /**
+ * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
+ * called when the quick access wallet requests dismissal.
+ */
+ @Override
+ public void dismissGlobalActionsMenu() {
+ dismissDialog();
+ }
+
+ /**
+ * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
+ * called when the quick access wallet requests that an intent be started (with lock screen
+ * shown first if needed).
+ */
+ @Override
+ public void startPendingIntentDismissingKeyguard(PendingIntent pendingIntent) {
+ mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent);
+ }
+
private final class PowerAction extends SinglePressAction implements LongPressAction {
private PowerAction() {
super(R.drawable.ic_lock_power_off,
@@ -916,7 +929,9 @@
}
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
public void onDismiss(DialogInterface dialog) {
if (mDialog == dialog) {
mDialog = null;
@@ -932,17 +947,19 @@
}
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
public void onShow(DialogInterface dialog) {
mMetricsLogger.visible(MetricsEvent.POWER_MENU);
}
/**
- * The adapter used for the list within the global actions dialog, taking
- * into account whether the keyguard is showing via
- * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
- * the device is provisioned
- * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
+ * The adapter used for the list within the global actions dialog, taking into account whether
+ * the keyguard is showing via
+ * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing}
+ * and whether the device is provisioned via
+ * {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
*/
public class MyAdapter extends MultiListAdapter {
private int countItems(boolean separated) {
@@ -1073,8 +1090,7 @@
*/
public interface Action {
/**
- * @return Text that will be announced when dialog is created. null
- * for none.
+ * @return Text that will be announced when dialog is created. null for none.
*/
CharSequence getLabelForAccessibility(Context context);
@@ -1083,15 +1099,13 @@
void onPress();
/**
- * @return whether this action should appear in the dialog when the keygaurd
- * is showing.
+ * @return whether this action should appear in the dialog when the keygaurd is showing.
*/
boolean showDuringKeyguard();
/**
- * @return whether this action should appear in the dialog before the
- * device is provisioned.onlongpress
- *
+ * @return whether this action should appear in the dialog before the device is
+ * provisioned.onlongpress
*/
boolean showBeforeProvisioning();
@@ -1110,8 +1124,7 @@
}
/**
- * A single press action maintains no state, just responds to a press
- * and takes an action.
+ * A single press action maintains no state, just responds to a press and takes an action.
*/
private abstract class SinglePressAction implements Action {
@@ -1185,8 +1198,8 @@
}
/**
- * A toggle action knows whether it is on or off, and displays an icon
- * and status message accordingly.
+ * A toggle action knows whether it is on or off, and displays an icon and status message
+ * accordingly.
*/
private static abstract class ToggleAction implements Action {
@@ -1236,8 +1249,7 @@
}
/**
- * Override to make changes to resource IDs just before creating the
- * View.
+ * Override to make changes to resource IDs just before creating the View.
*/
void willCreate() {
@@ -1293,9 +1305,9 @@
}
/**
- * Implementations may override this if their state can be in on of the intermediate
- * states until some notification is received (e.g airplane mode is 'turning off' until
- * we know the wireless connections are back online
+ * Implementations may override this if their state can be in on of the intermediate states
+ * until some notification is received (e.g airplane mode is 'turning off' until we know the
+ * wireless connections are back online
*
* @param buttonOn Whether the button was turned on or off
*/
@@ -1313,12 +1325,13 @@
private class AirplaneModeAction extends ToggleAction {
AirplaneModeAction() {
super(
- R.drawable.ic_lock_airplane_mode,
- R.drawable.ic_lock_airplane_mode_off,
- R.string.global_actions_toggle_airplane_mode,
- R.string.global_actions_airplane_mode_on_status,
- R.string.global_actions_airplane_mode_off_status);
+ R.drawable.ic_lock_airplane_mode,
+ R.drawable.ic_lock_airplane_mode_off,
+ R.string.global_actions_toggle_airplane_mode,
+ R.string.global_actions_airplane_mode_on_status,
+ R.string.global_actions_airplane_mode_off_status);
}
+
void onToggle(boolean on) {
if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
mIsWaitingForEcmExit = true;
@@ -1570,24 +1583,27 @@
private ResetOrientationData mResetOrientationData;
private boolean mHadTopUi;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final NotificationShadeDepthController mDepthController;
private final BlurUtils mBlurUtils;
private ControlsUiController mControlsUiController;
private ViewGroup mControlsView;
ActionsDialog(Context context, MyAdapter adapter,
- GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
+ GlobalActionsPanelPlugin.PanelViewController plugin,
+ NotificationShadeDepthController depthController,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- ControlsUiController controlsUiController) {
+ ControlsUiController controlsUiController, BlurUtils blurUtils) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
- mBlurUtils = blurUtils;
+ mDepthController = depthController;
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mControlsUiController = controlsUiController;
+ mBlurUtils = blurUtils;
// Window initialization
Window window = getWindow();
@@ -1601,11 +1617,11 @@
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
window.getAttributes().setFitInsetsTypes(0 /* types */);
setTitle(R.string.global_actions);
@@ -1696,7 +1712,8 @@
}
if (mBackgroundDrawable == null) {
mBackgroundDrawable = new ScrimDrawable();
- mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mScrimAlpha = mBlurUtils.supportsBlursOnWindows()
+ ? ScrimController.BLUR_SCRIM_ALPHA : ScrimController.BUSY_SCRIM_ALPHA;
}
getWindow().setBackgroundDrawable(mBackgroundDrawable);
}
@@ -1748,7 +1765,8 @@
/**
* Updates background and system bars according to current GradientColors.
- * @param colors Colors and hints to use.
+ *
+ * @param colors Colors and hints to use.
* @param animate Interpolates gradient if true, just sets otherwise.
*/
private void updateColors(GradientColors colors, boolean animate) {
@@ -1792,8 +1810,8 @@
float animatedValue = animation.getAnimatedFraction();
int alpha = (int) (animatedValue * mScrimAlpha * 255);
mBackgroundDrawable.setAlpha(alpha);
- mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(),
- mBlurUtils.radiusForRatio(animatedValue));
+ mDepthController.updateGlobalDialogVisibility(animatedValue,
+ mGlobalActionsLayout);
})
.start();
if (mControlsUiController != null) {
@@ -1822,8 +1840,8 @@
float animatedValue = 1f - animation.getAnimatedFraction();
int alpha = (int) (animatedValue * mScrimAlpha * 255);
mBackgroundDrawable.setAlpha(alpha);
- mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(),
- mBlurUtils.radiusForRatio(animatedValue));
+ mDepthController.updateGlobalDialogVisibility(animatedValue,
+ mGlobalActionsLayout);
})
.start();
dismissPanel();
@@ -1914,8 +1932,8 @@
}
/**
- * Determines whether or not the Global Actions menu should be forced to
- * use the newer grid-style layout.
+ * Determines whether or not the Global Actions menu should be forced to use the newer
+ * grid-style layout.
*/
private static boolean isForceGridEnabled(Context context) {
return isPanelDebugModeEnabled(context);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 280a248..12955a1 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -149,9 +149,9 @@
}
if (mBlurUtils.supportsBlursOnWindows()) {
- background.setAlpha((int) (ScrimController.BUSY_SCRIM_ALPHA * 255));
+ background.setAlpha((int) (ScrimController.BLUR_SCRIM_ALPHA * 255));
mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
- mBlurUtils.radiusForRatio(1));
+ mBlurUtils.blurRadiusOfRatio(1));
} else {
background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3d708a9..226ac16c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -91,8 +91,10 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -374,6 +376,7 @@
private boolean mLockLater;
private boolean mShowHomeOverLockscreen;
+ private boolean mInGestureNavigationMode;
private boolean mWakeAndUnlocking;
private IKeyguardDrawnCallback mDrawnCallback;
@@ -720,7 +723,8 @@
KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager,
@UiBackground Executor uiBgExecutor, PowerManager powerManager,
TrustManager trustManager,
- DeviceConfigProxy deviceConfig) {
+ DeviceConfigProxy deviceConfig,
+ NavigationModeController navigationModeController) {
super(context);
mFalsingManager = falsingManager;
mLockPatternUtils = lockPatternUtils;
@@ -742,6 +746,10 @@
DeviceConfig.NAMESPACE_SYSTEMUI,
mHandler::post,
mOnPropertiesChangedListener);
+ mInGestureNavigationMode =
+ QuickStepContract.isGesturalMode(navigationModeController.addListener(mode -> {
+ mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
+ }));
}
public void userActivity() {
@@ -2013,7 +2021,7 @@
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
- if (!mShowHomeOverLockscreen) {
+ if (!mShowHomeOverLockscreen || !mInGestureNavigationMode) {
flags |= StatusBarManager.DISABLE_HOME;
}
flags |= StatusBarManager.DISABLE_RECENT;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index ae380b7..c281ece 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -93,7 +93,7 @@
return mIatm.startActivityAsUser(
mContext.getIApplicationThread() /*caller*/,
mContext.getBasePackageName() /*callingPackage*/,
- mContext.getFeatureId() /*callingFeatureId*/,
+ mContext.getAttributionTag() /*callingAttributionTag*/,
intent /*intent*/,
intent.resolveTypeIfNeeded(mContext.getContentResolver()) /*resolvedType*/,
null /*resultTo*/,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 9be4786..7a63a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.DeviceConfigProxy;
@@ -64,7 +65,8 @@
PowerManager powerManager,
TrustManager trustManager,
@UiBackground Executor uiBgExecutor,
- DeviceConfigProxy deviceConfig) {
+ DeviceConfigProxy deviceConfig,
+ NavigationModeController navigationModeController) {
return new KeyguardViewMediator(
context,
falsingManager,
@@ -78,6 +80,7 @@
uiBgExecutor,
powerManager,
trustManager,
- deviceConfig);
+ deviceConfig,
+ navigationModeController);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index a161d03..e208ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -33,7 +33,6 @@
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
-import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -64,6 +63,7 @@
public class MediaControlPanel implements NotificationMediaManager.MediaListener {
private static final String TAG = "MediaControlPanel";
private final NotificationMediaManager mMediaManager;
+ private final Executor mForegroundExecutor;
private final Executor mBackgroundExecutor;
private Context mContext;
@@ -102,15 +102,18 @@
* @param manager
* @param layoutId layout resource to use for this control panel
* @param actionIds resource IDs for action buttons in the layout
+ * @param foregroundExecutor foreground executor
* @param backgroundExecutor background executor, used for processing artwork
*/
public MediaControlPanel(Context context, ViewGroup parent, NotificationMediaManager manager,
- @LayoutRes int layoutId, int[] actionIds, Executor backgroundExecutor) {
+ @LayoutRes int layoutId, int[] actionIds, Executor foregroundExecutor,
+ Executor backgroundExecutor) {
mContext = context;
LayoutInflater inflater = LayoutInflater.from(mContext);
mMediaNotifView = (LinearLayout) inflater.inflate(layoutId, parent, false);
mMediaManager = manager;
mActionIds = actionIds;
+ mForegroundExecutor = foregroundExecutor;
mBackgroundExecutor = backgroundExecutor;
}
@@ -176,15 +179,17 @@
mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
// Click action
- mMediaNotifView.setOnClickListener(v -> {
- try {
- contentIntent.send();
- // Also close shade
- mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent was canceled", e);
- }
- });
+ if (contentIntent != null) {
+ mMediaNotifView.setOnClickListener(v -> {
+ try {
+ contentIntent.send();
+ // Also close shade
+ mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent was canceled", e);
+ }
+ });
+ }
// App icon
ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
@@ -316,7 +321,7 @@
// Now that it's resized, update the UI
final RoundedBitmapDrawable result = roundedDrawable;
- albumView.getHandler().post(() -> {
+ mForegroundExecutor.execute(() -> {
if (result != null) {
albumView.setImageDrawable(result);
albumView.setVisibility(View.VISIBLE);
@@ -335,8 +340,7 @@
if (mSeamless == null) {
return;
}
- Handler handler = mSeamless.getHandler();
- handler.post(() -> {
+ mForegroundExecutor.execute(() -> {
updateChipInternal(device);
});
}
@@ -401,12 +405,15 @@
new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
mContext.sendBroadcast(intent);
} else {
- Log.d(TAG, "No receiver to restart");
// If we don't have a receiver, try relaunching the activity instead
- try {
- mController.getSessionActivity().send();
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent was canceled", e);
+ if (mController.getSessionActivity() != null) {
+ try {
+ mController.getSessionActivity().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent was canceled", e);
+ }
+ } else {
+ Log.e(TAG, "No receiver or activity to restart");
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 67802bc..4bfcf22 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -30,6 +30,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import javax.inject.Inject;
+
/**
* Controller class of PiP animations (both from and to PiP mode).
*/
@@ -37,7 +39,6 @@
private static final float FRACTION_START = 0f;
private static final float FRACTION_END = 1f;
- public static final int DURATION_DEFAULT_MS = 425;
public static final int ANIM_TYPE_BOUNDS = 0;
public static final int ANIM_TYPE_ALPHA = 1;
@@ -63,12 +64,15 @@
@interface TransitionDirection {}
private final Interpolator mFastOutSlowInInterpolator;
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private PipTransitionAnimator mCurrentAnimator;
- PipAnimationController(Context context) {
+ @Inject
+ PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
+ mSurfaceTransactionHelper = helper;
}
@SuppressWarnings("unchecked")
@@ -111,6 +115,7 @@
}
private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
+ animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
animator.setInterpolator(mFastOutSlowInInterpolator);
animator.setFloatValues(FRACTION_START, FRACTION_END);
return animator;
@@ -152,9 +157,10 @@
private T mEndValue;
private T mCurrentValue;
private PipAnimationCallback mPipAnimationCallback;
- private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
+ private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ mSurfaceControlTransactionFactory;
+ private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private @TransitionDirection int mTransitionDirection;
- private int mCornerRadius;
private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
Rect destinationBounds, T startValue, T endValue) {
@@ -243,15 +249,6 @@
mCurrentValue = value;
}
- int getCornerRadius() {
- return mCornerRadius;
- }
-
- PipTransitionAnimator<T> setCornerRadius(int cornerRadius) {
- mCornerRadius = cornerRadius;
- return this;
- }
-
boolean shouldApplyCornerRadius() {
return mTransitionDirection != TRANSITION_DIRECTION_TO_FULLSCREEN;
}
@@ -274,10 +271,19 @@
}
@VisibleForTesting
- void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) {
+ void setSurfaceControlTransactionFactory(
+ PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
mSurfaceControlTransactionFactory = factory;
}
+ PipSurfaceTransactionHelper getSurfaceTransactionHelper() {
+ return mSurfaceTransactionHelper;
+ }
+
+ void setSurfaceTransactionHelper(PipSurfaceTransactionHelper helper) {
+ mSurfaceTransactionHelper = helper;
+ }
+
abstract void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction);
@@ -290,14 +296,11 @@
SurfaceControl.Transaction tx, float fraction) {
final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
setCurrentValue(alpha);
- tx.setAlpha(leash, alpha);
+ getSurfaceTransactionHelper().alpha(tx, leash, alpha);
if (Float.compare(fraction, FRACTION_START) == 0) {
- // Ensure the start condition
- final Rect bounds = getDestinationBounds();
- tx.setPosition(leash, bounds.left, bounds.top)
- .setWindowCrop(leash, bounds.width(), bounds.height());
- tx.setCornerRadius(leash,
- shouldApplyCornerRadius() ? getCornerRadius() : 0);
+ getSurfaceTransactionHelper()
+ .crop(tx, leash, getDestinationBounds())
+ .round(tx, leash, shouldApplyCornerRadius());
}
tx.apply();
}
@@ -326,21 +329,16 @@
getCastedFractionValue(start.right, end.right, fraction),
getCastedFractionValue(start.bottom, end.bottom, fraction));
setCurrentValue(mTmpRect);
- tx.setPosition(leash, mTmpRect.left, mTmpRect.top)
- .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height());
+ getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
if (Float.compare(fraction, FRACTION_START) == 0) {
// Ensure the start condition
- tx.setAlpha(leash, 1f);
- tx.setCornerRadius(leash,
- shouldApplyCornerRadius() ? getCornerRadius() : 0);
+ getSurfaceTransactionHelper()
+ .alpha(tx, leash, 1f)
+ .round(tx, leash, shouldApplyCornerRadius());
}
tx.apply();
}
};
}
}
-
- interface SurfaceControlTransactionFactory {
- SurfaceControl.Transaction getTransaction();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index fb348f4..8be2502 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -67,7 +67,7 @@
private ComponentName mLastPipComponentName;
private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
- private Size mReentrySize = null;
+ private Size mReentrySize;
private float mDefaultAspectRatio;
private float mMinAspectRatio;
@@ -77,6 +77,7 @@
private int mDefaultMinSize;
private Point mScreenEdgeInsets;
private int mCurrentMinSize;
+ private Size mOverrideMinimalSize;
private boolean mIsImeShowing;
private int mImeHeight;
@@ -226,11 +227,14 @@
/**
* @return {@link Rect} of the destination PiP window bounds.
*/
- Rect getDestinationBounds(float aspectRatio, Rect bounds) {
+ Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) {
final Rect destinationBounds;
- final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
if (bounds == null) {
+ final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
destinationBounds = new Rect(defaultBounds);
+ if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) {
+ mOverrideMinimalSize = minimalSize;
+ }
} else {
destinationBounds = new Rect(bounds);
}
@@ -335,7 +339,6 @@
*/
private void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
boolean useCurrentMinEdgeSize) {
-
// Save the snap fraction and adjust the size based on the new aspect ratio.
final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
getMovementBounds(stackBounds));
@@ -354,10 +357,38 @@
final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
+ // apply the override minimal size if applicable, this minimal size is specified by app
+ if (mOverrideMinimalSize != null) {
+ transformBoundsToMinimalSize(stackBounds, aspectRatio, mOverrideMinimalSize);
+ }
mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
}
/**
+ * Transforms a given bounds to meet the minimal size constraints.
+ * This function assumes the given {@param stackBounds} qualifies {@param aspectRatio}.
+ */
+ private void transformBoundsToMinimalSize(Rect stackBounds, float aspectRatio,
+ Size minimalSize) {
+ if (minimalSize == null) return;
+ final Size adjustedMinimalSize;
+ final float minimalSizeAspectRatio =
+ minimalSize.getWidth() / (float) minimalSize.getHeight();
+ if (minimalSizeAspectRatio > aspectRatio) {
+ // minimal size is wider, fixed the width and increase the height
+ adjustedMinimalSize = new Size(
+ minimalSize.getWidth(), (int) (minimalSize.getWidth() / aspectRatio));
+ } else {
+ adjustedMinimalSize = new Size(
+ (int) (minimalSize.getHeight() * aspectRatio), minimalSize.getHeight());
+ }
+ final Rect containerBounds = new Rect(stackBounds);
+ Gravity.apply(mDefaultStackGravity,
+ adjustedMinimalSize.getWidth(), adjustedMinimalSize.getHeight(),
+ containerBounds, stackBounds);
+ }
+
+ /**
* @return the default bounds to show the PIP, if a {@param snapFraction} and {@param size} are
* provided, then it will apply the default bounds to the provided snap fraction and size.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
new file mode 100644
index 0000000..21f9301
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
@@ -0,0 +1,81 @@
+/*
+ * 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.pip;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import com.android.systemui.R;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
+ */
+@Singleton
+public class PipSurfaceTransactionHelper {
+
+ private final boolean mEnableCornerRadius;
+ private final int mCornerRadius;
+
+ @Inject
+ public PipSurfaceTransactionHelper(Context context) {
+ final Resources res = context.getResources();
+ mEnableCornerRadius = res.getBoolean(R.bool.config_pipEnableRoundCorner);
+ mCornerRadius = res.getDimensionPixelSize(R.dimen.pip_corner_radius);
+ }
+
+ /**
+ * Operates the alpha on a given transaction and leash
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ PipSurfaceTransactionHelper alpha(SurfaceControl.Transaction tx, SurfaceControl leash,
+ float alpha) {
+ tx.setAlpha(leash, alpha);
+ return this;
+ }
+
+ /**
+ * Operates the crop (and position) on a given transaction and leash
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ PipSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect destinationBounds) {
+ tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
+ .setPosition(leash, destinationBounds.left, destinationBounds.top);
+ return this;
+ }
+
+ /**
+ * Operates the round corner radius on a given transaction and leash
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash,
+ boolean applyCornerRadius) {
+ if (mEnableCornerRadius) {
+ tx.setCornerRadius(leash, applyCornerRadius ? mCornerRadius : 0);
+ }
+ return this;
+ }
+
+ interface SurfaceControlTransactionFactory {
+ SurfaceControl.Transaction getTransaction();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 00b977e..dc1b5d7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -18,7 +18,6 @@
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
-import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
@@ -31,12 +30,14 @@
import android.app.ITaskOrganizerController;
import android.app.PictureInPictureParams;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Size;
import android.view.ITaskOrganizer;
import android.view.IWindowContainer;
import android.view.SurfaceControl;
@@ -79,7 +80,8 @@
private final PipAnimationController mPipAnimationController;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
private final Rect mLastReportedBounds = new Rect();
- private final int mCornerRadius;
+ private final int mEnterExitAnimationDuration;
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>();
// These callbacks are called on the update thread
@@ -172,14 +174,20 @@
private SurfaceControl mLeash;
private boolean mInPip;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+ private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ mSurfaceControlTransactionFactory;
- public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) {
+ public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
+ @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper) {
mMainHandler = new Handler(Looper.getMainLooper());
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
mPipBoundsHandler = boundsHandler;
- mPipAnimationController = new PipAnimationController(context);
- mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+ mEnterExitAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipResizeAnimationDuration);
+ mSurfaceTransactionHelper = surfaceTransactionHelper;
+ mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper);
+ mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
}
public Handler getUpdateHandler() {
@@ -202,26 +210,12 @@
mOneShotAnimationType = animationType;
}
- /**
- * Callback to issue the final {@link WindowContainerTransaction} on end of movements.
- * @param destinationBounds the final bounds.
- */
- public void onMotionMovementEnd(Rect destinationBounds) {
- try {
- mLastReportedBounds.set(destinationBounds);
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mToken, destinationBounds);
- mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to apply window container transaction", e);
- }
- }
-
@Override
public void taskAppeared(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(info, "Requires RunningTaskInfo");
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+ getAspectRatioOrDefault(info.pictureInPictureParams),
+ null /* bounds */, getMinimalSize(info.topActivityInfo));
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
mTaskInfo = info;
mToken = mTaskInfo.token;
@@ -235,14 +229,13 @@
mBoundsToRestore.put(mToken.asBinder(), currentBounds);
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
scheduleAnimateResizePip(currentBounds, destinationBounds,
- TRANSITION_DIRECTION_TO_PIP, DURATION_DEFAULT_MS, null);
+ TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
mUpdateHandler.post(() -> mPipAnimationController
.getAnimator(mLeash, destinationBounds, 0f, 1f)
.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
- .setCornerRadius(mCornerRadius)
.setPipAnimationCallback(mPipAnimationCallback)
- .setDuration(DURATION_DEFAULT_MS)
+ .setDuration(mEnterExitAnimationDuration)
.start());
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
@@ -258,9 +251,9 @@
Log.wtf(TAG, "Unrecognized token: " + token);
return;
}
- final Rect boundsToRestore = mBoundsToRestore.remove(mToken.asBinder());
+ final Rect boundsToRestore = mBoundsToRestore.remove(token.asBinder());
scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore,
- TRANSITION_DIRECTION_TO_FULLSCREEN, DURATION_DEFAULT_MS, null);
+ TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null);
mInPip = false;
}
@@ -276,9 +269,10 @@
return;
}
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(newParams), null /* bounds */);
+ getAspectRatioOrDefault(newParams),
+ null /* bounds */, getMinimalSize(info.topActivityInfo));
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
- scheduleAnimateResizePip(destinationBounds, DURATION_DEFAULT_MS, null);
+ scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, null);
}
/**
@@ -305,7 +299,6 @@
private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction, int durationMs,
Consumer<Rect> updateBoundsCallback) {
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
if (!mInPip) {
// Ignore animation when we are no longer in PIP
return;
@@ -324,7 +317,6 @@
* {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
*/
public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) {
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
args.arg2 = toBounds;
@@ -332,22 +324,20 @@
}
/**
- * Finish a intermediate resize operation. This is expected to be called after
+ * Finish an intermediate resize operation. This is expected to be called after
* {@link #scheduleResizePip}.
*/
public void scheduleFinishResizePip(Rect destinationBounds) {
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
- SurfaceControl.Transaction tx = new SurfaceControl.Transaction()
- .setPosition(mLeash, destinationBounds.left, destinationBounds.top)
- .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
- .setCornerRadius(mLeash, mInPip ? mCornerRadius : 0);
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper
+ .crop(tx, mLeash, destinationBounds)
+ .round(tx, mLeash, mInPip);
scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
}
private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
Rect destinationBounds, @PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) {
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
args.arg2 = tx;
@@ -365,7 +355,6 @@
// Ignore offsets when we are no longer in PIP
return;
}
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
args.arg2 = originalBounds;
@@ -394,17 +383,16 @@
throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
+ "directly");
}
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
// Could happen when dismissPip
if (mToken == null || mLeash == null) {
Log.w(TAG, "Abort animation, invalid leash");
return;
}
- new SurfaceControl.Transaction()
- .setPosition(mLeash, destinationBounds.left, destinationBounds.top)
- .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
- .setCornerRadius(mLeash, mInPip ? mCornerRadius : 0)
- .apply();
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper
+ .crop(tx, mLeash, destinationBounds)
+ .round(tx, mLeash, mInPip);
+ tx.apply();
}
private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
@@ -415,11 +403,16 @@
}
mLastReportedBounds.set(destinationBounds);
try {
+ // If we are animating to fullscreen, then we need to reset the override bounds on the
+ // task to ensure that the task "matches" the parent's bounds
+ Rect taskBounds = direction == TRANSITION_DIRECTION_TO_FULLSCREEN
+ ? null
+ : destinationBounds;
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (direction == TRANSITION_DIRECTION_TO_PIP) {
- wct.scheduleFinishEnterPip(mToken, destinationBounds);
+ wct.scheduleFinishEnterPip(mToken, taskBounds);
} else {
- wct.setBounds(mToken, destinationBounds);
+ wct.setBounds(mToken, taskBounds);
}
wct.setBoundsChangeTransaction(mToken, tx);
mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
@@ -442,12 +435,19 @@
mUpdateHandler.post(() -> mPipAnimationController
.getAnimator(mLeash, currentBounds, destinationBounds)
.setTransitionDirection(direction)
- .setCornerRadius(mCornerRadius)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
.start());
}
+ private Size getMinimalSize(ActivityInfo activityInfo) {
+ if (activityInfo == null || activityInfo.windowLayout == null) {
+ return null;
+ }
+ final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
+ return new Size(windowLayout.minWidth, windowLayout.minHeight);
+ }
+
private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
return params == null
? mPipBoundsHandler.getDefaultAspectRatio()
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
index b09d6e1..7dfd99c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
@@ -44,7 +44,7 @@
try {
// Dismiss the PiP once the user disables the app ops setting for that package
final Pair<ComponentName, Integer> topPipActivityInfo =
- PipUtils.getTopPinnedActivity(mContext, mActivityManager);
+ PipUtils.getTopPipActivity(mContext, mActivityManager);
if (topPipActivityInfo.first != null) {
final ApplicationInfo appInfo = mContext.getPackageManager()
.getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 1fdf92e..020627a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -42,6 +42,7 @@
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -115,7 +116,7 @@
@Override
public void onActivityUnpinned() {
- final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity(
+ final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPipActivity(
mContext, mActivityManager);
final ComponentName topActivity = topPipActivityInfo.first;
mMenuController.onActivityUnpinned();
@@ -210,7 +211,8 @@
FloatingContentCoordinator floatingContentCoordinator,
DeviceConfigProxy deviceConfig,
PipBoundsHandler pipBoundsHandler,
- PipSnapAlgorithm pipSnapAlgorithm) {
+ PipSnapAlgorithm pipSnapAlgorithm,
+ PipSurfaceTransactionHelper surfaceTransactionHelper) {
mContext = context;
mActivityManager = ActivityManager.getService();
@@ -224,7 +226,8 @@
final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
mPipBoundsHandler = pipBoundsHandler;
- mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+ mPipTaskOrganizer = new PipTaskOrganizer(context, pipBoundsHandler,
+ surfaceTransactionHelper);
mPipTaskOrganizer.registerPipTransitionCallback(this);
mInputConsumerController = InputConsumerController.getPipInputConsumer();
mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
@@ -237,6 +240,12 @@
mTouchHandler.getMotionHelper());
displayController.addDisplayChangingController(mRotationController);
+ // Ensure that we have the display info in case we get calls to update the bounds before the
+ // listener calls back
+ final DisplayInfo displayInfo = new DisplayInfo();
+ context.getDisplay().getDisplayInfo(displayInfo);
+ mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+
try {
ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
mPipTaskOrganizer, WINDOWING_MODE_PINNED);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index e57b416..849a62a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -231,7 +231,7 @@
*/
private void resolveActiveMediaController(List<MediaController> controllers) {
if (controllers != null) {
- final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext,
+ final ComponentName topActivity = PipUtils.getTopPipActivity(mContext,
mActivityManager).first;
if (topActivity != null) {
for (int i = 0; i < controllers.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index fc04f79..2b9b171 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -569,7 +569,7 @@
private void showSettings() {
final Pair<ComponentName, Integer> topPipActivityInfo =
- PipUtils.getTopPinnedActivity(this, ActivityManager.getService());
+ PipUtils.getTopPipActivity(this, ActivityManager.getService());
if (topPipActivityInfo.first != null) {
final UserHandle user = UserHandle.of(topPipActivityInfo.second);
final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 33760be..449a2bc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -78,10 +78,10 @@
private final Rect mBounds = new Rect();
/** The bounds within which PIP's top-left coordinate is allowed to move. */
- private Rect mMovementBounds = new Rect();
+ private final Rect mMovementBounds = new Rect();
/** The region that all of PIP must stay within. */
- private Rect mFloatingAllowedArea = new Rect();
+ private final Rect mFloatingAllowedArea = new Rect();
/**
* Bounds that are animated using the physics animator.
@@ -89,7 +89,7 @@
private final Rect mAnimatedBounds = new Rect();
/** The destination bounds to which PIP is animating. */
- private Rect mAnimatingToBounds = new Rect();
+ private final Rect mAnimatingToBounds = new Rect();
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -120,7 +120,7 @@
new PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
- private final Consumer<Rect> mUpdateBoundsCallback = (toBounds) -> mBounds.set(toBounds);
+ private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set;
public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
@@ -426,7 +426,7 @@
cancelAnimations();
mAnimatedBoundsPhysicsAnimator
- .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
+ .withEndActions(() -> mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds))
.addUpdateListener(mResizePipUpdateListener)
.start();
}
@@ -437,7 +437,7 @@
* {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
*/
private void setAnimatingToBounds(Rect bounds) {
- mAnimatingToBounds = bounds;
+ mAnimatingToBounds.set(bounds);
mFloatingContentCoordinator.onContentMoved(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 8fff419..25acce6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -65,6 +65,7 @@
private final PointF mDownPoint = new PointF();
private final Point mMaxSize = new Point();
private final Point mMinSize = new Point();
+ private final Rect mLastResizeBounds = new Rect();
private final Rect mTmpBounds = new Rect();
private final int mDelta;
@@ -187,17 +188,13 @@
private void onMotionEvent(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
+ mLastResizeBounds.setEmpty();
mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
if (mAllowGesture) {
mDownPoint.set(ev.getX(), ev.getY());
}
} else if (mAllowGesture) {
- final Rect currentPipBounds = mMotionHelper.getBounds();
- Rect newSize = TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x,
- mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, mMinSize.y, mMaxSize,
- true, true);
- mPipBoundsHandler.transformBoundsToAspectRatio(newSize);
switch (action) {
case MotionEvent.ACTION_POINTER_DOWN:
// We do not support multi touch for resizing via drag
@@ -206,11 +203,16 @@
case MotionEvent.ACTION_MOVE:
// Capture inputs
mInputMonitor.pilferPointers();
- //TODO: Actually do resize here.
+ final Rect currentPipBounds = mMotionHelper.getBounds();
+ mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
+ mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+ mMinSize.y, mMaxSize, true, true));
+ mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
+ mPipTaskOrganizer.scheduleResizePip(mLastResizeBounds, null);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- //TODO: Finish resize operation here.
+ mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds);
mMotionHelper.synchronizePinnedStackBounds();
mCtrlType = CTRL_NONE;
mAllowGesture = false;
@@ -223,7 +225,7 @@
mMaxSize.set(maxX, maxY);
}
- void updateMiniSize(int minX, int minY) {
+ void updateMinSize(int minX, int minY) {
mMinSize.set(minX, minY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 90db91a..9b67d80 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -31,6 +31,7 @@
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
import android.util.Size;
import android.view.IPinnedStackController;
import android.view.InputEvent;
@@ -41,6 +42,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.systemui.R;
import com.android.systemui.pip.PipBoundsHandler;
@@ -73,7 +75,7 @@
private final Context mContext;
private final IActivityManager mActivityManager;
private final PipBoundsHandler mPipBoundsHandler;
- private final PipResizeGestureHandler mPipResizeGestureHandler;
+ private PipResizeGestureHandler mPipResizeGestureHandler;
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
@@ -84,14 +86,17 @@
// The current movement bounds
private Rect mMovementBounds = new Rect();
+ // The current resized bounds, changed by user resize.
+ // This is used during expand/un-expand to save/restore the user's resized size.
+ @VisibleForTesting Rect mResizedBounds = new Rect();
// The reference inset bounds, used to determine the dismiss fraction
private Rect mInsetBounds = new Rect();
// The reference bounds used to calculate the normal/expanded target bounds
private Rect mNormalBounds = new Rect();
- private Rect mNormalMovementBounds = new Rect();
+ @VisibleForTesting Rect mNormalMovementBounds = new Rect();
private Rect mExpandedBounds = new Rect();
- private Rect mExpandedMovementBounds = new Rect();
+ @VisibleForTesting Rect mExpandedMovementBounds = new Rect();
private int mExpandedShortestEdgeSize;
// Used to workaround an issue where the WM rotation happens before we are notified, allowing
@@ -126,7 +131,7 @@
private final PipTouchState mTouchState;
private final FlingAnimationUtils mFlingAnimationUtils;
private final FloatingContentCoordinator mFloatingContentCoordinator;
- private final PipMotionHelper mMotionHelper;
+ private PipMotionHelper mMotionHelper;
private PipTouchGesture mGesture;
// Temp vars
@@ -148,8 +153,11 @@
@Override
public void onPipDismiss() {
- MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext,
- PipUtils.getTopPinnedActivity(mContext, mActivityManager));
+ Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext,
+ mActivityManager);
+ if (topPipActivity.first != null) {
+ MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, topPipActivity);
+ }
mMotionHelper.dismissPip();
}
@@ -236,14 +244,15 @@
mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
}
+ mResizedBounds.setEmpty();
mPipResizeGestureHandler.onActivityUnpinned();
}
public void onPinnedStackAnimationEnded() {
// Always synchronize the motion helper bounds once PiP animations finish
mMotionHelper.synchronizePinnedStackBounds();
- mPipResizeGestureHandler.updateMiniSize(mMotionHelper.getBounds().width(),
- mMotionHelper.getBounds().height());
+ updateMovementBounds();
+ mResizedBounds.set(mMotionHelper.getBounds());
if (mShowPipMenuOnAnimationEnd) {
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -288,11 +297,13 @@
Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
- mPipResizeGestureHandler.updateMaxSize(expandedSize.getWidth(), expandedSize.getHeight());
Rect expandedMovementBounds = new Rect();
mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
bottomOffset);
+ mPipResizeGestureHandler.updateMinSize(mNormalBounds.width(), mNormalBounds.height());
+ mPipResizeGestureHandler.updateMaxSize(mExpandedBounds.width(), mExpandedBounds.height());
+
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
int extraOffset = Math.max(
@@ -328,7 +339,7 @@
mExpandedMovementBounds = expandedMovementBounds;
mDisplayRotation = displayRotation;
mInsetBounds.set(insetBounds);
- updateMovementBounds(mMenuState);
+ updateMovementBounds();
mMovementBoundsExtraOffsets = extraOffset;
// If we have a deferred resize, apply it now
@@ -388,7 +399,7 @@
case MotionEvent.ACTION_UP: {
// Update the movement bounds again if the state has changed since the user started
// dragging (ie. when the IME shows)
- updateMovementBounds(mMenuState);
+ updateMovementBounds();
if (mGesture.onUp(mTouchState)) {
break;
@@ -486,9 +497,11 @@
if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
// Save the current snap fraction and if we do not drag or move the PiP, then
// we store back to this snap fraction. Otherwise, we'll reset the snap
- // fraction and snap to the closest edge
- Rect expandedBounds = new Rect(mExpandedBounds);
+ // fraction and snap to the closest edge.
+ // Also save the current resized bounds so when the menu disappears, we can restore it.
if (resize) {
+ mResizedBounds.set(mMotionHelper.getBounds());
+ Rect expandedBounds = new Rect(mExpandedBounds);
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
mMovementBounds, mExpandedMovementBounds);
}
@@ -516,9 +529,12 @@
}
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
- Rect normalBounds = new Rect(mNormalBounds);
- mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, false /* immediate */);
+ Rect restoreBounds = new Rect(mResizedBounds);
+ Rect restoredMovementBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds,
+ restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
+ mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
+ restoredMovementBounds, mMovementBounds, false /* immediate */);
mSavedSnapFraction = -1f;
}
} else {
@@ -529,7 +545,7 @@
}
}
mMenuState = menuState;
- updateMovementBounds(menuState);
+ updateMovementBounds();
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
// as well, or it can't handle a11y focus and pip menu can't perform any action.
onRegistrationChanged(menuState == MENU_STATE_NONE);
@@ -545,6 +561,21 @@
return mMotionHelper;
}
+ @VisibleForTesting
+ PipResizeGestureHandler getPipResizeGestureHandler() {
+ return mPipResizeGestureHandler;
+ }
+
+ @VisibleForTesting
+ void setPipResizeGestureHandler(PipResizeGestureHandler pipResizeGestureHandler) {
+ mPipResizeGestureHandler = pipResizeGestureHandler;
+ }
+
+ @VisibleForTesting
+ void setPipMotionHelper(PipMotionHelper pipMotionHelper) {
+ mMotionHelper = pipMotionHelper;
+ }
+
/**
* @return the unexpanded bounds.
*/
@@ -653,7 +684,7 @@
// Check if the user dragged or flung the PiP offscreen to dismiss it
if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
- PipUtils.getTopPinnedActivity(mContext, mActivityManager));
+ PipUtils.getTopPipActivity(mContext, mActivityManager));
mMotionHelper.animateDismiss(
vel.x, vel.y,
PipTouchHandler.this::updateDismissFraction /* updateAction */);
@@ -705,14 +736,14 @@
* Updates the current movement bounds based on whether the menu is currently visible and
* resized.
*/
- private void updateMovementBounds(int menuState) {
- boolean isMenuExpanded = menuState == MENU_STATE_FULL;
- mMovementBounds = isMenuExpanded && willResizeMenu()
- ? mExpandedMovementBounds
- : mNormalMovementBounds;
- mPipBoundsHandler.setMinEdgeSize(
- isMenuExpanded ? mExpandedShortestEdgeSize : 0);
+ private void updateMovementBounds() {
+ mSnapAlgorithm.getMovementBounds(mMotionHelper.getBounds(), mInsetBounds,
+ mMovementBounds, mIsImeShowing ? mImeHeight : 0);
mMotionHelper.setCurrentMovementBounds(mMovementBounds);
+
+ boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
+ mPipBoundsHandler.setMinEdgeSize(
+ isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
index 1ed1904..4cfec01 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -36,7 +36,7 @@
* @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
* The component name may be null if no such activity exists.
*/
- public static Pair<ComponentName, Integer> getTopPinnedActivity(Context context,
+ public static Pair<ComponentName, Integer> getTopPipActivity(Context context,
IActivityManager activityManager) {
try {
final String sysUiPackageName = context.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 0c5a4d7..050acd5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -20,8 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS;
-
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityTaskManager;
@@ -53,6 +51,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
@@ -136,6 +135,7 @@
private String[] mLastPackagesResourceGranted;
private PipNotification mPipNotification;
private ParceledListSlice mCustomActions;
+ private int mResizeAnimationDuration;
// Used to calculate the movement bounds
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
@@ -230,7 +230,8 @@
@Inject
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
- PipBoundsHandler pipBoundsHandler) {
+ PipBoundsHandler pipBoundsHandler,
+ PipSurfaceTransactionHelper surfaceTransactionHelper) {
if (mInitialized) {
return;
}
@@ -238,7 +239,10 @@
mInitialized = true;
mContext = context;
mPipBoundsHandler = pipBoundsHandler;
- mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+ mResizeAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipResizeAnimationDuration);
+ mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler,
+ surfaceTransactionHelper);
mPipTaskOrganizer.registerPipTransitionCallback(this);
mActivityTaskManager = ActivityTaskManager.getService();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -436,7 +440,8 @@
mCurrentPipBounds = mPipBounds;
break;
}
- mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, DURATION_DEFAULT_MS, null);
+ mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, mResizeAnimationDuration,
+ null);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 3cf0718..ece1ce8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -28,6 +28,7 @@
void forceCollapsePanels();
void openPanels();
Context getContext();
+ Context getUserContext();
QSLogger getQSLogger();
Collection<QSTile> getTiles();
void addCallback(Callback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 837256b..d5e5b10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -55,11 +55,13 @@
* @param context
* @param parent
* @param manager
+ * @param foregroundExecutor
* @param backgroundExecutor
*/
public QSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
- Executor backgroundExecutor) {
- super(context, parent, manager, R.layout.qs_media_panel, QS_ACTION_IDS, backgroundExecutor);
+ Executor foregroundExecutor, Executor backgroundExecutor) {
+ super(context, parent, manager, R.layout.qs_media_panel, QS_ACTION_IDS, foregroundExecutor,
+ backgroundExecutor);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 9ab4714..5ccf8c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -44,6 +44,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.InfoMediaManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.Dependency;
@@ -51,6 +53,7 @@
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
@@ -98,6 +101,8 @@
private final LinearLayout mMediaCarousel;
private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
private final NotificationMediaManager mNotificationMediaManager;
+ private final LocalBluetoothManager mLocalBluetoothManager;
+ private final Executor mForegroundExecutor;
private final Executor mBackgroundExecutor;
private LocalMediaManager mLocalMediaManager;
private MediaDevice mDevice;
@@ -157,14 +162,18 @@
BroadcastDispatcher broadcastDispatcher,
QSLogger qsLogger,
NotificationMediaManager notificationMediaManager,
- @Background Executor backgroundExecutor
+ @Main Executor foregroundExecutor,
+ @Background Executor backgroundExecutor,
+ @Nullable LocalBluetoothManager localBluetoothManager
) {
super(context, attrs);
mContext = context;
mQSLogger = qsLogger;
mDumpManager = dumpManager;
mNotificationMediaManager = notificationMediaManager;
+ mForegroundExecutor = foregroundExecutor;
mBackgroundExecutor = backgroundExecutor;
+ mLocalBluetoothManager = localBluetoothManager;
setOrientation(VERTICAL);
@@ -265,7 +274,7 @@
if (player == null) {
Log.d(TAG, "creating new player");
player = new QSMediaPlayer(mContext, this, mNotificationMediaManager,
- mBackgroundExecutor);
+ mForegroundExecutor, mBackgroundExecutor);
if (player.isPlaying()) {
mMediaCarousel.addView(player.getView(), 0, lp); // add in front
@@ -286,7 +295,9 @@
// Set up listener for device changes
// TODO: integrate with MediaTransferManager?
- mLocalMediaManager = new LocalMediaManager(mContext, null, null);
+ InfoMediaManager imm =
+ new InfoMediaManager(mContext, null, null, mLocalBluetoothManager);
+ mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, imm, null);
mLocalMediaManager.startScan();
mDevice = mLocalMediaManager.getCurrentConnectedDevice();
mLocalMediaManager.registerCallback(mDeviceCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index fab7191..9e8eb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -45,7 +45,6 @@
import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -98,7 +97,7 @@
@Inject
public QSTileHost(Context context,
StatusBarIconController iconController,
- QSFactoryImpl defaultFactory,
+ QSFactory defaultFactory,
@Main Handler mainHandler,
@Background Looper bgLooper,
PluginManager pluginManager,
@@ -120,7 +119,6 @@
mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
mStatusBarOptional = statusBarOptional;
- defaultFactory.setHost(this);
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
mDumpManager.registerDumpable(TAG, this);
@@ -211,10 +209,12 @@
return mContext;
}
+ @Override
public Context getUserContext() {
return mUserContext;
}
+ @Override
public TileServices getTileServices() {
return mServices;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index 4512afb..0c50194 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -48,12 +48,13 @@
* @param context
* @param parent
* @param manager
+ * @param foregroundExecutor
* @param backgroundExecutor
*/
public QuickQSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
- Executor backgroundExecutor) {
+ Executor foregroundExecutor, Executor backgroundExecutor) {
super(context, parent, manager, R.layout.qqs_media_panel, QQS_ACTION_IDS,
- backgroundExecutor);
+ foregroundExecutor, backgroundExecutor);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 3da767e..be01d75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -18,6 +18,7 @@
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -26,10 +27,12 @@
import android.view.View;
import android.widget.LinearLayout;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
@@ -77,10 +80,12 @@
BroadcastDispatcher broadcastDispatcher,
QSLogger qsLogger,
NotificationMediaManager notificationMediaManager,
- @Background Executor backgroundExecutor
+ @Main Executor foregroundExecutor,
+ @Background Executor backgroundExecutor,
+ @Nullable LocalBluetoothManager localBluetoothManager
) {
super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, notificationMediaManager,
- backgroundExecutor);
+ foregroundExecutor, backgroundExecutor, localBluetoothManager);
if (mFooter != null) {
removeView(mFooter.getView());
}
@@ -100,7 +105,7 @@
int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout,
- notificationMediaManager, backgroundExecutor);
+ notificationMediaManager, foregroundExecutor, backgroundExecutor);
LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
lp2.setMarginEnd(marginSize);
lp2.setMarginStart(0);
@@ -155,8 +160,6 @@
Dependency.get(TunerService.class).removeTunable(mNumTiles);
}
-
-
@Override
protected String getDumpableTag() {
return TAG;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index d422dd7..11b625f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -436,6 +436,10 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ // Handle padding of QuickStatusBarHeader
+ setPadding(mRoundedCornerPadding, getPaddingTop(), mRoundedCornerPadding,
+ getPaddingBottom());
+
// Handle padding of SystemIconsView
DisplayCutout cutout = insets.getDisplayCutout();
Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
@@ -450,8 +454,11 @@
int statusBarPaddingRight = isLayoutRtl()
? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start)
: getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end);
- mSystemIconsView.setPadding(padding.first + statusBarPaddingLeft, waterfallTopInset,
- padding.second + statusBarPaddingRight, 0);
+ mSystemIconsView.setPadding(
+ Math.max(padding.first + statusBarPaddingLeft - mRoundedCornerPadding, 0),
+ waterfallTopInset,
+ Math.max(padding.second + statusBarPaddingRight - mRoundedCornerPadding, 0),
+ 0);
return super.onApplyWindowInsets(insets);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 3b27fb7..08c8f86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -46,7 +46,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -79,7 +79,7 @@
private boolean mIsTokenGranted;
private boolean mIsShowingDialog;
- private CustomTile(QSTileHost host, String action, Context userContext) {
+ private CustomTile(QSHost host, String action, Context userContext) {
super(host);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
@@ -392,7 +392,7 @@
return ComponentName.unflattenFromString(action);
}
- public static CustomTile create(QSTileHost host, String spec, Context userContext) {
+ public static CustomTile create(QSHost host, String spec, Context userContext) {
if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
throw new IllegalArgumentException("Bad custom tile spec: " + spec);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 1b8717b..c182a58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -24,7 +24,7 @@
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tiles.AirplaneModeTile;
import com.android.systemui.qs.tiles.BatterySaverTile;
@@ -51,6 +51,8 @@
import javax.inject.Provider;
import javax.inject.Singleton;
+import dagger.Lazy;
+
@Singleton
public class QSFactoryImpl implements QSFactory {
@@ -77,10 +79,11 @@
private final Provider<UiModeNightTile> mUiModeNightTileProvider;
private final Provider<ScreenRecordTile> mScreenRecordTileProvider;
- private QSTileHost mHost;
+ private final Lazy<QSHost> mQsHostLazy;
@Inject
- public QSFactoryImpl(Provider<WifiTile> wifiTileProvider,
+ public QSFactoryImpl(Lazy<QSHost> qsHostLazy,
+ Provider<WifiTile> wifiTileProvider,
Provider<BluetoothTile> bluetoothTileProvider,
Provider<CellularTile> cellularTileProvider,
Provider<DndTile> dndTileProvider,
@@ -100,6 +103,7 @@
Provider<GarbageMonitor.MemoryTile> memoryTileProvider,
Provider<UiModeNightTile> uiModeNightTileProvider,
Provider<ScreenRecordTile> screenRecordTileProvider) {
+ mQsHostLazy = qsHostLazy;
mWifiTileProvider = wifiTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
mCellularTileProvider = cellularTileProvider;
@@ -122,10 +126,6 @@
mScreenRecordTileProvider = screenRecordTileProvider;
}
- public void setHost(QSTileHost host) {
- mHost = host;
- }
-
public QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
@@ -179,7 +179,8 @@
// Custom tiles
if (tileSpec.startsWith(CustomTile.PREFIX)) {
- return CustomTile.create(mHost, tileSpec, mHost.getUserContext());
+ return CustomTile.create(mQsHostLazy.get(), tileSpec,
+ mQsHostLazy.get().getUserContext());
}
// Debug tiles.
@@ -196,7 +197,7 @@
@Override
public QSTileView createTileView(QSTile tile, boolean collapsedView) {
- Context context = new ContextThemeWrapper(mHost.getContext(), R.style.qs_theme);
+ Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
QSIconView icon = tile.createTileView(context);
if (collapsedView) {
return new QSTileBaseView(context, icon, collapsedView);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 7c770f4..1780fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -347,6 +347,7 @@
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
// TODO use taskId and visibleInsets
+ clearScreenshot("new screenshot requested");
takeScreenshot(screenshot, finisher, screenshotScreenBounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index fb68153..d7eab3a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -31,7 +31,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.provider.Settings;
-import android.util.Log;
import android.util.Slog;
import android.view.IWindowContainer;
import android.view.LayoutInflater;
@@ -73,7 +72,7 @@
DisplayController.OnDisplaysChangedListener {
private static final String TAG = "Divider";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
static final int DEFAULT_APP_TRANSITION_DURATION = 336;
static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
@@ -148,6 +147,8 @@
* regardless of what has focus.
*/
private boolean mTargetShown = false;
+ private float mTargetPrimaryDim = 0.f;
+ private float mTargetSecondaryDim = 0.f;
// The following are the current (most recent) states set during animation
/** {@code true} if the secondary split has IME focus. */
@@ -171,6 +172,9 @@
@Nullable
private ValueAnimator mAnimation = null;
+ private boolean mPaused = true;
+ private boolean mPausedTargetAdjusted = false;
+
private boolean getSecondaryHasFocus(int displayId) {
try {
IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController()
@@ -183,18 +187,47 @@
return false;
}
+ private void updateDimTargets() {
+ final boolean splitIsVisible = !mView.isHidden();
+ mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
+ ? ADJUSTED_NONFOCUS_DIM : 0.f;
+ mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible)
+ ? ADJUSTED_NONFOCUS_DIM : 0.f;
+ }
+
@Override
public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
boolean imeShouldShow, SurfaceControl.Transaction t) {
+ if (!inSplitMode()) {
+ return;
+ }
+ final boolean splitIsVisible = !mView.isHidden();
mSecondaryHasFocus = getSecondaryHasFocus(displayId);
- mTargetAdjusted = imeShouldShow && mSecondaryHasFocus
+ final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
&& !mSplitLayout.mDisplayLayout.isLandscape();
mHiddenTop = hiddenTop;
mShownTop = shownTop;
mTargetShown = imeShouldShow;
if (mLastAdjustTop < 0) {
mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
+ } else {
+ // Check for an "interruption" of an existing animation. In this case, we need to
+ // fake-flip the last-known state direction so that the animation completes in the
+ // other direction.
+ if (mTargetAdjusted != targetAdjusted && targetAdjusted == mAdjusted) {
+ if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
+ mAdjusted = mTargetAdjusted;
+ }
+ }
}
+ if (mPaused) {
+ mPausedTargetAdjusted = targetAdjusted;
+ if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState());
+ return;
+ }
+ mTargetAdjusted = targetAdjusted;
+ updateDimTargets();
+ if (DEBUG) Slog.d(TAG, " ime starting. vis:" + splitIsVisible + " " + dumpState());
if (mAnimation != null || (mImeWasShown && imeShouldShow
&& mTargetAdjusted != mAdjusted)) {
// We need to animate adjustment independently of the IME position, so
@@ -202,7 +235,11 @@
// different split's editor has gained focus while the IME is still visible.
startAsyncAnimation();
}
- updateImeAdjustState();
+ if (splitIsVisible) {
+ // If split is hidden, we don't want to trigger any relayouts that would cause the
+ // divider to show again.
+ updateImeAdjustState();
+ }
}
private void updateImeAdjustState() {
@@ -245,7 +282,7 @@
@Override
public void onImePositionChanged(int displayId, int imeTop,
SurfaceControl.Transaction t) {
- if (mAnimation != null) {
+ if (mAnimation != null || !inSplitMode() || mPaused) {
// Not synchronized with IME anymore, so return.
return;
}
@@ -257,7 +294,7 @@
@Override
public void onImeEndPositioning(int displayId, boolean cancelled,
SurfaceControl.Transaction t) {
- if (mAnimation != null) {
+ if (mAnimation != null || !inSplitMode() || mPaused) {
// Not synchronized with IME anymore, so return.
return;
}
@@ -265,7 +302,7 @@
}
private void onProgress(float progress, SurfaceControl.Transaction t) {
- if (mTargetAdjusted != mAdjusted) {
+ if (mTargetAdjusted != mAdjusted && !mPaused) {
final float fraction = mTargetAdjusted ? progress : 1.f - progress;
mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop);
mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop);
@@ -273,14 +310,10 @@
mSplitLayout.mAdjustedSecondary);
}
final float invProg = 1.f - progress;
- final float targetPrimaryDim =
- (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
- final float targetSecondaryDim =
- (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
mView.setResizeDimLayer(t, true /* primary */,
- mLastPrimaryDim * invProg + progress * targetPrimaryDim);
+ mLastPrimaryDim * invProg + progress * mTargetPrimaryDim);
mView.setResizeDimLayer(t, false /* primary */,
- mLastSecondaryDim * invProg + progress * targetSecondaryDim);
+ mLastSecondaryDim * invProg + progress * mTargetSecondaryDim);
}
private void onEnd(boolean cancelled, SurfaceControl.Transaction t) {
@@ -289,10 +322,8 @@
mAdjusted = mTargetAdjusted;
mImeWasShown = mTargetShown;
mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop;
- mLastPrimaryDim =
- (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
- mLastSecondaryDim =
- (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f;
+ mLastPrimaryDim = mTargetPrimaryDim;
+ mLastSecondaryDim = mTargetSecondaryDim;
}
}
@@ -334,6 +365,52 @@
});
mAnimation.start();
}
+
+ private String dumpState() {
+ return "top:" + mHiddenTop + "->" + mShownTop
+ + " adj:" + mAdjusted + "->" + mTargetAdjusted + "(" + mLastAdjustTop + ")"
+ + " shw:" + mImeWasShown + "->" + mTargetShown
+ + " dims:" + mLastPrimaryDim + "," + mLastSecondaryDim
+ + "->" + mTargetPrimaryDim + "," + mTargetSecondaryDim
+ + " shf:" + mSecondaryHasFocus + " desync:" + (mAnimation != null)
+ + " paus:" + mPaused + "[" + mPausedTargetAdjusted + "]";
+ }
+
+ /** Completely aborts/resets adjustment state */
+ public void pause(int displayId) {
+ if (DEBUG) Slog.d(TAG, "ime pause posting " + dumpState());
+ mHandler.post(() -> {
+ if (DEBUG) Slog.d(TAG, "ime pause run posted " + dumpState());
+ if (mPaused) {
+ return;
+ }
+ mPaused = true;
+ mPausedTargetAdjusted = mTargetAdjusted;
+ mTargetAdjusted = false;
+ mTargetPrimaryDim = mTargetSecondaryDim = 0.f;
+ updateImeAdjustState();
+ startAsyncAnimation();
+ });
+ }
+
+ public void resume(int displayId) {
+ if (DEBUG) Slog.d(TAG, "ime resume posting " + dumpState());
+ mHandler.post(() -> {
+ if (DEBUG) Slog.d(TAG, "ime resume run posted " + dumpState());
+ if (!mPaused) {
+ return;
+ }
+ mPaused = false;
+ mTargetAdjusted = mPausedTargetAdjusted;
+ updateDimTargets();
+ if ((mTargetAdjusted != mAdjusted) && !mMinimized && mView != null) {
+ // End unminimize animations since they conflict with adjustment animations.
+ mView.finishAnimations();
+ }
+ updateImeAdjustState();
+ startAsyncAnimation();
+ });
+ }
}
private final DividerImeController mImePositionProcessor = new DividerImeController();
@@ -485,6 +562,7 @@
}
void updateVisibility(final boolean visible) {
+ if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
if (mVisible != visible) {
mVisible = visible;
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
@@ -511,12 +589,21 @@
/** Switch to minimized state if appropriate */
public void setMinimized(final boolean minimized) {
+ if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
mHandler.post(() -> {
+ if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
+ if (!mVisible) {
+ return;
+ }
setHomeMinimized(minimized, mHomeStackResizable);
});
}
private void setHomeMinimized(final boolean minimized, boolean homeStackResizable) {
+ if (DEBUG) {
+ Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:"
+ + mHomeStackResizable + "->" + homeStackResizable + " split:" + inSplitMode());
+ }
WindowContainerTransaction wct = new WindowContainerTransaction();
// Update minimized state
if (mMinimized != minimized) {
@@ -536,7 +623,17 @@
// Sync state to DividerView if it exists.
if (mView != null) {
+ final int displayId = mView.getDisplay() != null
+ ? mView.getDisplay().getDisplayId() : DEFAULT_DISPLAY;
+ // pause ime here (before updateMinimizedDockedStack)
+ if (mMinimized) {
+ mImePositionProcessor.pause(displayId);
+ }
mView.setMinimizedDockStack(minimized, getAnimDuration(), homeStackResizable);
+ if (!mMinimized) {
+ // afterwards so it can end any animations started in view
+ mImePositionProcessor.resume(displayId);
+ }
}
updateTouchable();
WindowManagerProxy.applyContainerTransaction(wct);
@@ -641,7 +738,7 @@
if (!inSplitMode()) {
// Wasn't in split-mode yet, so enter now.
if (DEBUG) {
- Log.d(TAG, " entering split mode with minimized=true");
+ Slog.d(TAG, " entering split mode with minimized=true");
}
updateVisibility(true /* visible */);
}
@@ -652,7 +749,7 @@
if (!inSplitMode()) {
// Wasn't in split-mode, so enter now.
if (DEBUG) {
- Log.d(TAG, " enter split mode unminimized ");
+ Slog.d(TAG, " enter split mode unminimized ");
}
updateVisibility(true /* visible */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 477cbb7..131f4e1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -34,6 +34,7 @@
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
+import android.util.Slog;
import android.view.Choreographer;
import android.view.Display;
import android.view.InsetsState;
@@ -67,12 +68,15 @@
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import java.util.function.Consumer;
+
/**
* Docked stack divider.
*/
public class DividerView extends FrameLayout implements OnTouchListener,
OnComputeInternalInsetsListener {
private static final String TAG = "DividerView";
+ private static final boolean DEBUG = Divider.DEBUG;
public interface DividerCallbacks {
void onDraggingStart();
@@ -165,6 +169,10 @@
// The view is removed or in the process of been removed from the system.
private boolean mRemoved;
+ // Whether the surface for this view has been hidden regardless of actual visibility. This is
+ // used interact with keyguard.
+ private boolean mSurfaceHidden = false;
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -414,6 +422,10 @@
/** Unlike setVisible, this directly hides the surface without changing view visibility. */
void setHidden(boolean hidden) {
+ if (mSurfaceHidden == hidden) {
+ return;
+ }
+ mSurfaceHidden = hidden;
post(() -> {
final SurfaceControl sc = getWindowSurfaceControl();
if (sc == null) {
@@ -430,6 +442,10 @@
});
}
+ boolean isHidden() {
+ return mSurfaceHidden;
+ }
+
public boolean startDragging(boolean animate, boolean touching) {
cancelFlingAnimation();
if (touching) {
@@ -615,6 +631,7 @@
cancelFlingAnimation();
updateDockSide();
}
+ if (DEBUG) Slog.d(TAG, "Getting fling " + position + "->" + snapTarget.position);
final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
@@ -622,16 +639,21 @@
? TASK_POSITION_SAME
: snapTarget.taskPosition,
snapTarget));
- Runnable endAction = () -> {
+ Consumer<Boolean> endAction = cancelled -> {
+ if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction);
+ final boolean wasMinimizeInteraction = mIsInMinimizeInteraction;
+ // Reset minimized divider position after unminimized state animation finishes.
+ if (!cancelled && !mDockedStackMinimized && mIsInMinimizeInteraction) {
+ mIsInMinimizeInteraction = false;
+ }
boolean dismissed = commitSnapFlags(snapTarget);
mWindowManagerProxy.setResizing(false);
updateDockSide();
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
mExitAnimationRunning = false;
- if (!dismissed) {
- WindowManagerProxy.applyResizeSplits((mIsInMinimizeInteraction
- ? mSnapTargetBeforeMinimized : snapTarget).position, mSplitLayout);
+ if (!dismissed && !wasMinimizeInteraction) {
+ WindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout);
}
if (mCallback != null) {
mCallback.onDraggingEnd();
@@ -655,12 +677,6 @@
}
}
};
- Runnable notCancelledEndAction = () -> {
- // Reset minimized divider position after unminimized state animation finishes
- if (!mDockedStackMinimized && mIsInMinimizeInteraction) {
- mIsInMinimizeInteraction = false;
- }
- };
anim.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@@ -682,15 +698,11 @@
delay = mSfChoreographer.getSurfaceFlingerOffsetMs();
}
if (delay == 0) {
- if (!mCancelled) {
- notCancelledEndAction.run();
- }
- endAction.run();
+ endAction.accept(mCancelled);
} else {
- if (!mCancelled) {
- mHandler.postDelayed(notCancelledEndAction, delay);
- }
- mHandler.postDelayed(endAction, delay);
+ final Boolean cancelled = mCancelled;
+ if (DEBUG) Slog.d(TAG, "Posting endFling " + cancelled + " d:" + delay + "ms");
+ mHandler.postDelayed(() -> endAction.accept(cancelled), delay);
}
}
});
@@ -887,6 +899,7 @@
public void setMinimizedDockStack(boolean minimized, long animDuration,
boolean isHomeStackResizable) {
+ if (DEBUG) Slog.d(TAG, "setMinDock: " + mDockedStackMinimized + "->" + minimized);
mHomeStackResizable = isHomeStackResizable;
updateDockSide();
if (!isHomeStackResizable) {
@@ -934,6 +947,15 @@
.start();
}
+ // Needed to end any currently playing animations when they might compete with other anims
+ // (specifically, IME adjust animation immediately after leaving minimized). Someday maybe
+ // these can be unified, but not today.
+ void finishAnimations() {
+ if (mCurrentAnimator != null) {
+ mCurrentAnimator.end();
+ }
+ }
+
public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
if (mAdjustedForIme == adjustedForIme) {
return;
@@ -1049,6 +1071,11 @@
mDividerPositionX = dockedRect.right;
mDividerPositionY = dockedRect.bottom;
+ if (DEBUG) {
+ Slog.d(TAG, "Resizing split surfaces: " + dockedRect + " " + dockedTaskRect
+ + " " + otherRect + " " + otherTaskRect);
+ }
+
t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top);
Rect crop = new Rect(dockedRect);
crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0),
@@ -1071,7 +1098,7 @@
void setResizeDimLayer(Transaction t, boolean primary, float alpha) {
SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim;
- if (alpha <= 0.f) {
+ if (alpha <= 0.001f) {
t.hide(dim);
} else {
t.setAlpha(dim, alpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 34c0860..8669804 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -40,8 +40,10 @@
) : Dumpable {
private val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
private val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
- private val blurSysProp = SystemProperties
+ private val blurSupportedSysProp = SystemProperties
.getBoolean("ro.surface_flinger.supports_background_blur", false)
+ private val blurDisabledSysProp = SystemProperties
+ .getBoolean("persist.sys.sf.disable_blurs", false)
init {
dumpManager.registerDumpable(javaClass.name, this)
@@ -50,7 +52,7 @@
/**
* Translates a ratio from 0 to 1 to a blur radius in pixels.
*/
- fun radiusForRatio(ratio: Float): Int {
+ fun blurRadiusOfRatio(ratio: Float): Int {
if (ratio == 0f) {
return 0
}
@@ -58,6 +60,17 @@
}
/**
+ * Translates a blur radius in pixels to a ratio between 0 to 1.
+ */
+ fun ratioOfBlurRadius(blur: Int): Float {
+ if (blur == 0) {
+ return 0f
+ }
+ return MathUtils.map(minBlurRadius.toFloat(), maxBlurRadius.toFloat(),
+ 0f /* maxStart */, 1f /* maxStop */, blur.toFloat())
+ }
+
+ /**
* Applies background blurs to a {@link ViewRootImpl}.
*
* @param viewRootImpl The window root.
@@ -86,7 +99,7 @@
* @return {@code true} when supported.
*/
open fun supportsBlursOnWindows(): Boolean {
- return blurSysProp && ActivityManager.isHighEndGfx()
+ return blurSupportedSysProp && !blurDisabledSysProp && ActivityManager.isHighEndGfx()
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
@@ -95,7 +108,8 @@
it.increaseIndent()
it.println("minBlurRadius: $minBlurRadius")
it.println("maxBlurRadius: $maxBlurRadius")
- it.println("blurSysProp: $blurSysProp")
+ it.println("blurSupportedSysProp: $blurSupportedSysProp")
+ it.println("blurDisabledSysProp: $blurDisabledSysProp")
it.println("supportsBlursOnWindows: ${supportsBlursOnWindows()}")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3fe348f..94afde78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -262,7 +262,8 @@
default void showAuthenticationDialog(Bundle bundle,
IBiometricServiceReceiverInternal receiver, int biometricModality,
- boolean requireConfirmation, int userId, String opPackageName) { }
+ boolean requireConfirmation, int userId, String opPackageName,
+ long operationId) { }
default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
default void onBiometricError(int modality, int error, int vendorCode) { }
@@ -780,7 +781,8 @@
@Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
+ long operationId) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -789,6 +791,7 @@
args.arg3 = requireConfirmation;
args.argi2 = userId;
args.arg4 = opPackageName;
+ args.arg5 = operationId;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
}
@@ -1164,7 +1167,8 @@
someArgs.argi1 /* biometricModality */,
(boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */,
- (String) someArgs.arg4 /* opPackageName */);
+ (String) someArgs.arg4 /* opPackageName */,
+ (long) someArgs.arg5 /* operationId */);
}
someArgs.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 4597b16..b33424c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -33,6 +33,7 @@
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener;
import java.util.List;
@@ -159,20 +160,30 @@
}
public void setEntry(NotificationEntry entry) {
- if (entry != null) {
- mShowingEntry = entry;
+ if (mShowingEntry != null) {
+ mShowingEntry.removeOnSensitivityChangedListener(mOnSensitivityChangedListener);
+ }
+ mShowingEntry = entry;
+
+ if (mShowingEntry != null) {
CharSequence text = entry.headsUpStatusBarText;
if (entry.isSensitive()) {
text = entry.headsUpStatusBarTextPublic;
}
mTextView.setText(text);
- mShowingEntry.setOnSensitiveChangedListener(() -> setEntry(entry));
- } else if (mShowingEntry != null){
- mShowingEntry.setOnSensitiveChangedListener(null);
- mShowingEntry = null;
+ mShowingEntry.addOnSensitivityChangedListener(mOnSensitivityChangedListener);
}
}
+ private final OnSensitivityChangedListener mOnSensitivityChangedListener = entry -> {
+ if (entry != mShowingEntry) {
+ throw new IllegalStateException("Got a sensitivity change for " + entry
+ + " but mShowingEntry is " + mShowingEntry);
+ }
+ // Update the text
+ setEntry(entry);
+ };
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
index 18574f0..ac3523b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
@@ -32,6 +32,8 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.InfoMediaManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.media.MediaOutputSliceConstants;
@@ -106,7 +108,9 @@
public MediaTransferManager(Context context) {
mContext = context;
mActivityStarter = Dependency.get(ActivityStarter.class);
- mLocalMediaManager = new LocalMediaManager(mContext, null, null);
+ LocalBluetoothManager lbm = Dependency.get(LocalBluetoothManager.class);
+ InfoMediaManager imm = new InfoMediaManager(mContext, null, null, lbm);
+ mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, null);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 0bfcdbd..4759d56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -23,6 +23,7 @@
import android.text.TextUtils;
import android.view.NotificationHeaderView;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@@ -182,24 +183,24 @@
private void sanitizeChild(View child) {
if (child != null) {
- NotificationHeaderView header = (NotificationHeaderView) child.findViewById(
+ ViewGroup header = child.findViewById(
com.android.internal.R.id.notification_header);
sanitizeHeader(header);
}
}
- private void sanitizeHeader(NotificationHeaderView rowHeader) {
+ private void sanitizeHeader(ViewGroup rowHeader) {
if (rowHeader == null) {
return;
}
final int childCount = rowHeader.getChildCount();
View time = rowHeader.findViewById(com.android.internal.R.id.time);
boolean hasVisibleText = false;
- for (int i = 1; i < childCount - 1 ; i++) {
+ for (int i = 0; i < childCount; i++) {
View child = rowHeader.getChildAt(i);
if (child instanceof TextView
&& child.getVisibility() != View.GONE
- && !mDividers.contains(Integer.valueOf(child.getId()))
+ && !mDividers.contains(child.getId())
&& child != time) {
hasVisibleText = true;
break;
@@ -212,14 +213,14 @@
time.setVisibility(timeVisibility);
View left = null;
View right;
- for (int i = 1; i < childCount - 1 ; i++) {
+ for (int i = 0; i < childCount; i++) {
View child = rowHeader.getChildAt(i);
- if (mDividers.contains(Integer.valueOf(child.getId()))) {
+ if (mDividers.contains(child.getId())) {
boolean visible = false;
// Lets find the item to the right
- for (i++; i < childCount - 1; i++) {
+ for (i++; i < childCount; i++) {
right = rowHeader.getChildAt(i);
- if (mDividers.contains(Integer.valueOf(right.getId()))) {
+ if (mDividers.contains(right.getId())) {
// A divider was found, this needs to be hidden
i--;
break;
@@ -276,14 +277,18 @@
if (!mApply) {
return;
}
- NotificationHeaderView header = row.getContractedNotificationHeader();
- if (header == null) {
- // No header found. We still consider this to be the same to avoid weird flickering
+ View contractedChild = row.getPrivateLayout().getContractedChild();
+ if (contractedChild == null) {
+ return;
+ }
+ View ownView = contractedChild.findViewById(mId);
+ if (ownView == null) {
+ // No view found. We still consider this to be the same to avoid weird flickering
// when for example showing an undo notification
return;
}
Object childData = mExtractor == null ? null : mExtractor.extractData(row);
- mApply = mComparator.compare(mParentView, header.findViewById(mId),
+ mApply = mComparator.compare(mParentView, ownView,
mParentData, childData);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 047edd2..72d9d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -206,7 +206,8 @@
false,
false,
false,
- null
+ null,
+ false
);
}
return ranking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 87be739..d8fdf92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -70,6 +70,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -262,11 +263,11 @@
synchronized (mEntryManager) {
NotificationEntry entry = mEntryManager
.getActiveNotificationUnfiltered(mMediaNotificationKey);
- if (entry == null || entry.expandedIcon == null) {
+ if (entry == null || entry.getIcons().getShelfIcon() == null) {
return null;
}
- return entry.expandedIcon.getSourceIcon();
+ return entry.getIcons().getShelfIcon().getSourceIcon();
}
}
@@ -284,8 +285,7 @@
boolean metaDataChanged = false;
synchronized (mEntryManager) {
- Set<NotificationEntry> allNotifications =
- mEntryManager.getPendingAndActiveNotifications();
+ Collection<NotificationEntry> allNotifications = mEntryManager.getAllNotifs();
// Promote the media notification with a controller in 'playing' state, if any.
NotificationEntry mediaNotification = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
similarity index 66%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 6e905a3..8945f36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -19,8 +19,10 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
+import android.app.WallpaperManager
import android.view.Choreographer
import android.view.View
+import androidx.annotation.VisibleForTesting
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
@@ -28,6 +30,7 @@
import com.android.systemui.Dumpable
import com.android.systemui.Interpolators
import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.NotificationShadeWindowController
@@ -43,67 +46,78 @@
* Controller responsible for statusbar window blur.
*/
@Singleton
-class NotificationShadeWindowBlurController @Inject constructor(
- private val statusBarStateController: SysuiStatusBarStateController,
+class NotificationShadeDepthController @Inject constructor(
+ private val statusBarStateController: StatusBarStateController,
private val blurUtils: BlurUtils,
private val biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
- private val notificationShadeWindowController: NotificationShadeWindowController,
private val choreographer: Choreographer,
+ private val wallpaperManager: WallpaperManager,
+ private val notificationShadeWindowController: NotificationShadeWindowController,
dumpManager: DumpManager
) : PanelExpansionListener, Dumpable {
companion object {
private const val WAKE_UP_ANIMATION_ENABLED = true
- private const val SHADE_BLUR_ENABLED = true
}
lateinit var root: View
+ private var blurRoot: View? = null
private var keyguardAnimator: Animator? = null
private var notificationAnimator: Animator? = null
private var updateScheduled: Boolean = false
- private var shadeExpansion = 1.0f
- private val shadeSpring = SpringAnimation(this, object :
- FloatPropertyCompat<NotificationShadeWindowBlurController>("shadeBlurRadius") {
- override fun setValue(rect: NotificationShadeWindowBlurController?, value: Float) {
+ private var shadeExpansion = 0f
+ @VisibleForTesting
+ var shadeSpring = SpringAnimation(this, object :
+ FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
+ override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
shadeBlurRadius = value.toInt()
}
- override fun getValue(rect: NotificationShadeWindowBlurController?): Float {
+ override fun getValue(rect: NotificationShadeDepthController?): Float {
return shadeBlurRadius.toFloat()
}
})
+ private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE
+
+ /**
+ * Radius that we're animating to.
+ */
+ private var pendingShadeBlurRadius = -1
+
+ /**
+ * Shade blur radius on the current frame.
+ */
private var shadeBlurRadius = 0
set(value) {
if (field == value) return
field = value
scheduleUpdate()
}
- private var wakeAndUnlockBlurRadius = 0
- set(value) {
- if (field == value) return
- field = value
- scheduleUpdate()
- }
- private var incomingNotificationBlurRadius = 0
- set(value) {
- if (field == value) return
- field = value
- scheduleUpdate()
- }
/**
+ * Blur radius of the wake-up animation on this frame.
+ */
+ private var wakeAndUnlockBlurRadius = 0
+ set(value) {
+ if (field == value) return
+ field = value
+ scheduleUpdate()
+ }
+ private var globalDialogVisibility = 0f
+
+ /**
* Callback that updates the window blur value and is called only once per frame.
*/
private val updateBlurCallback = Choreographer.FrameCallback {
updateScheduled = false
- var notificationBlur = 0
- if (statusBarStateController.state == StatusBarState.KEYGUARD) {
- notificationBlur = (incomingNotificationBlurRadius * shadeExpansion).toInt()
- }
-
- val blur = max(max(shadeBlurRadius, wakeAndUnlockBlurRadius), notificationBlur)
- blurUtils.applyBlur(root.viewRootImpl, blur)
+ val blur = max(shadeBlurRadius,
+ max(wakeAndUnlockBlurRadius, blurUtils.blurRadiusOfRatio(globalDialogVisibility)))
+ blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
+ val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility)
+ wallpaperManager.setWallpaperZoomOut(root.windowToken,
+ zoomInterpolator.getInterpolation(rawZoom))
+ notificationShadeWindowController.setBackgroundBlurRadius(blur)
}
/**
@@ -123,7 +137,7 @@
interpolator = Interpolators.DECELERATE_QUINT
addUpdateListener { animation: ValueAnimator ->
wakeAndUnlockBlurRadius =
- blurUtils.radiusForRatio(animation.animatedValue as Float)
+ blurUtils.blurRadiusOfRatio(animation.animatedValue as Float)
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
@@ -143,6 +157,18 @@
}
}
+ private val statusBarStateCallback = object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ updateShadeBlur()
+ }
+
+ override fun onDozingChanged(isDozing: Boolean) {
+ if (isDozing && shadeSpring.isRunning) {
+ shadeSpring.skipToEnd()
+ }
+ }
+ }
+
init {
dumpManager.registerDumpable(javaClass.name, this)
if (WAKE_UP_ANIMATION_ENABLED) {
@@ -151,41 +177,57 @@
shadeSpring.spring = SpringForce(0.0f)
shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
+ shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 }
+ statusBarStateController.addCallback(statusBarStateCallback)
}
/**
* Update blurs when pulling down the shade
*/
override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
- if (!SHADE_BLUR_ENABLED) {
+ if (expansion == shadeExpansion) {
return
}
+ shadeExpansion = expansion
+ updateShadeBlur()
+ }
+ private fun updateShadeBlur() {
var newBlur = 0
if (statusBarStateController.state == StatusBarState.SHADE) {
- newBlur = blurUtils.radiusForRatio(expansion)
+ newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
}
- if (shadeBlurRadius == newBlur) {
+ if (pendingShadeBlurRadius == newBlur) {
return
}
+ pendingShadeBlurRadius = newBlur
shadeSpring.animateToFinalPosition(newBlur.toFloat())
}
- private fun scheduleUpdate() {
+ private fun scheduleUpdate(viewToBlur: View? = null) {
if (updateScheduled) {
return
}
updateScheduled = true
+ blurRoot = viewToBlur
choreographer.postFrameCallback(updateBlurCallback)
}
+ fun updateGlobalDialogVisibility(visibility: Float, dialogView: View) {
+ if (visibility == globalDialogVisibility) {
+ return
+ }
+ globalDialogVisibility = visibility
+ scheduleUpdate(dialogView)
+ }
+
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
- IndentingPrintWriter(pw, " ").use {
+ IndentingPrintWriter(pw, " ").let {
it.println("StatusBarWindowBlurController:")
it.increaseIndent()
it.println("shadeBlurRadius: $shadeBlurRadius")
it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 1a8454c..d7f2ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -329,7 +329,7 @@
expandableRow.setAboveShelf(false);
}
if (notGoneIndex == 0) {
- StatusBarIconView icon = expandableRow.getEntry().expandedIcon;
+ StatusBarIconView icon = expandableRow.getEntry().getIcons().getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon);
// The icon state might be null in rare cases where the notification is actually
// added to the layout, but not to the shelf. An example are replied messages,
@@ -432,7 +432,7 @@
// if the shelf is clipped, lets make sure we also clip the icon
maxTop = Math.max(maxTop, getTranslationY() + getClipTopAmount());
}
- StatusBarIconView icon = row.getEntry().expandedIcon;
+ StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon();
float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) {
int top = (int) (maxTop - shelfIconPosition);
@@ -444,7 +444,7 @@
}
private void updateContinuousClipping(final ExpandableNotificationRow row) {
- StatusBarIconView icon = row.getEntry().expandedIcon;
+ StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon();
boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 7b5a70e..2a45bc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -41,6 +41,7 @@
private static final int TAG_CONTAINS_TRANSFORMED_VIEW = R.id.contains_transformed_view;
private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>();
+ private ArraySet<Integer> mKeysTransformingToSimilar = new ArraySet<>();
private ArrayMap<Integer, CustomTransformation> mCustomTransformations = new ArrayMap<>();
private ValueAnimator mViewTransformationAnimation;
@@ -48,8 +49,22 @@
mTransformedViews.put(key, transformedView);
}
+ /**
+ * Add a view that transforms to a similar sibling, meaning that we should consider any mapping
+ * found treated as the same viewType. This is useful for imageViews, where it's hard to compare
+ * if the source images are the same when they are bitmap based.
+ *
+ * @param key The key how this is added
+ * @param transformedView the view that is added
+ */
+ public void addViewTransformingToSimilar(int key, View transformedView) {
+ addTransformedView(key, transformedView);
+ mKeysTransformingToSimilar.add(key);
+ }
+
public void reset() {
mTransformedViews.clear();
+ mKeysTransformingToSimilar.clear();
}
public void setCustomTransformation(CustomTransformation transformation, int viewType) {
@@ -60,7 +75,11 @@
public TransformState getCurrentState(int fadingView) {
View view = mTransformedViews.get(fadingView);
if (view != null && view.getVisibility() != View.GONE) {
- return TransformState.createFrom(view, this);
+ TransformState transformState = TransformState.createFrom(view, this);
+ if (mKeysTransformingToSimilar.contains(fadingView)) {
+ transformState.setIsSameAsAnyView(true);
+ }
+ return transformState;
}
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index b732966..9383f53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -21,9 +21,9 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.widget.IMessagingLayout;
import com.android.internal.widget.MessagingGroup;
import com.android.internal.widget.MessagingImageMessage;
-import com.android.internal.widget.MessagingLayout;
import com.android.internal.widget.MessagingLinearLayout;
import com.android.internal.widget.MessagingMessage;
import com.android.internal.widget.MessagingPropertyAnimator;
@@ -41,7 +41,7 @@
private static Pools.SimplePool<MessagingLayoutTransformState> sInstancePool
= new Pools.SimplePool<>(40);
private MessagingLinearLayout mMessageContainer;
- private MessagingLayout mMessagingLayout;
+ private IMessagingLayout mMessagingLayout;
private HashMap<MessagingGroup, MessagingGroup> mGroupMap = new HashMap<>();
private float mRelativeTranslationOffset;
@@ -266,8 +266,9 @@
transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */
useLinearTransformation);
boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild;
- if (transformationAmount == 0.0f && otherIsIsolated) {
- ownGroup.setTransformingImages(true);
+ if (transformationAmount == 0.0f
+ && (otherIsIsolated || otherGroup.isSingleLine())) {
+ ownGroup.setClippingDisabled(true);
}
if (otherChild == null) {
child.setTranslationY(previousTranslation);
@@ -291,11 +292,20 @@
if (useLinearTransformation) {
ownState.setDefaultInterpolator(Interpolators.LINEAR);
}
- ownState.setIsSameAsAnyView(sameAsAny);
+ ownState.setIsSameAsAnyView(sameAsAny && !isGone(otherView));
if (to) {
if (otherView != null) {
TransformState otherState = TransformState.createFrom(otherView, mTransformInfo);
- ownState.transformViewTo(otherState, transformationAmount);
+ if (!isGone(otherView)) {
+ ownState.transformViewTo(otherState, transformationAmount);
+ } else {
+ if (!isGone(ownView)) {
+ ownState.disappear(transformationAmount, null);
+ }
+ // We still want to transform vertically if the view is gone,
+ // since avatars serve as anchors for the rest of the layout transition
+ ownState.transformViewVerticalTo(otherState, transformationAmount);
+ }
otherState.recycle();
} else {
ownState.disappear(transformationAmount, null);
@@ -303,7 +313,16 @@
} else {
if (otherView != null) {
TransformState otherState = TransformState.createFrom(otherView, mTransformInfo);
- ownState.transformViewFrom(otherState, transformationAmount);
+ if (!isGone(otherView)) {
+ ownState.transformViewFrom(otherState, transformationAmount);
+ } else {
+ if (!isGone(ownView)) {
+ ownState.appear(transformationAmount, null);
+ }
+ // We still want to transform vertically if the view is gone,
+ // since avatars serve as anchors for the rest of the layout transition
+ ownState.transformViewVerticalFrom(otherState, transformationAmount);
+ }
otherState.recycle();
} else {
ownState.appear(transformationAmount, null);
@@ -337,6 +356,9 @@
}
private boolean isGone(View view) {
+ if (view == null) {
+ return true;
+ }
if (view.getVisibility() == View.GONE) {
return true;
}
@@ -408,7 +430,7 @@
ownGroup.getMessageContainer().setTranslationY(0);
ownGroup.getSenderView().setTranslationY(0);
}
- ownGroup.setTransformingImages(false);
+ ownGroup.setClippingDisabled(false);
ownGroup.updateClipRect();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 7f0479c..c6d84ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -29,6 +29,7 @@
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -56,9 +57,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -105,6 +106,10 @@
*/
public static final int UNDEFINED_DISMISS_REASON = 0;
+ private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
+ private final Set<NotificationEntry> mReadOnlyAllNotifications =
+ Collections.unmodifiableSet(mAllNotifications);
+
/** Pending notifications are ones awaiting inflation */
@VisibleForTesting
protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
@@ -468,6 +473,8 @@
entry.removeRow();
}
+ mAllNotifications.remove(entry);
+
// Let's remove the children if this was a summary
handleGroupSummaryRemoved(key);
removeVisibleNotification(key);
@@ -548,6 +555,7 @@
notification,
ranking,
mFgsFeatureController.isForegroundServiceDismissalEnabled());
+ mAllNotifications.add(entry);
mLeakDetector.trackInstance(entry);
@@ -709,15 +717,6 @@
}
/**
- * @return all notifications we're currently aware of (both pending and active notifications)
- */
- public Set<NotificationEntry> getPendingAndActiveNotifications() {
- Set<NotificationEntry> allNotifs = new HashSet<>(mPendingNotifications.values());
- allNotifs.addAll(mSortedAndFiltered);
- return allNotifs;
- }
-
- /**
* Use this method to retrieve a notification entry that has been prepared for presentation.
* Note that the notification may be filtered out and never shown to the user.
*
@@ -842,7 +841,7 @@
private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
pw.print(indent);
- pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.icon);
+ pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.getIcons().getStatusBarIcon());
StatusBarNotification n = e.getSbn();
pw.print(indent);
pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
@@ -861,6 +860,15 @@
return mReadOnlyNotifications;
}
+ /**
+ * Returns a collections containing ALL notifications we know about, including ones that are
+ * hidden or for other users. See {@link CommonNotifCollection#getAllNotifs()}.
+ */
+ @Override
+ public Collection<NotificationEntry> getAllNotifs() {
+ return mReadOnlyAllNotifications;
+ }
+
/** @return A count of the active notifications */
public int getActiveNotificationsCount() {
return mReadOnlyNotifications.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 8a23e37..2747696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification
import android.animation.ObjectAnimator
-import android.content.Context
import android.util.FloatProperty
import com.android.systemui.Interpolators
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -26,10 +25,10 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.PanelExpansionListener
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import javax.inject.Inject
@@ -37,15 +36,14 @@
@Singleton
class NotificationWakeUpCoordinator @Inject constructor(
- private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
- private val statusBarStateController: StatusBarStateController,
- private val bypassController: KeyguardBypassController,
- private val dozeParameters: DozeParameters)
- : OnHeadsUpChangedListener, StatusBarStateController.StateListener,
- PanelExpansionListener {
+ private val mHeadsUpManager: HeadsUpManager,
+ private val statusBarStateController: StatusBarStateController,
+ private val bypassController: KeyguardBypassController,
+ private val dozeParameters: DozeParameters
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
- private val mNotificationVisibility
- = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") {
+ private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
+ "notificationVisibility") {
override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) {
coordinator.setVisibilityAmount(value)
@@ -78,10 +76,10 @@
field = value
willWakeUp = false
if (value) {
- if (mNotificationsVisible && !mNotificationsVisibleForExpansion
- && !bypassController.bypassEnabled) {
+ if (mNotificationsVisible && !mNotificationsVisibleForExpansion &&
+ !bypassController.bypassEnabled) {
// We're waking up while pulsing, let's make sure the animation looks nice
- mStackScroller.wakeUpFromPulse();
+ mStackScroller.wakeUpFromPulse()
}
if (bypassController.bypassEnabled && !mNotificationsVisible) {
// Let's make sure our huns become visible once we are waking up in case
@@ -100,7 +98,7 @@
}
private var collapsedEnoughToHide: Boolean = false
- lateinit var iconAreaController : NotificationIconAreaController
+ lateinit var iconAreaController: NotificationIconAreaController
var pulsing: Boolean = false
set(value) {
@@ -132,8 +130,8 @@
var canShow = pulsing
if (bypassController.bypassEnabled) {
// We also allow pulsing on the lock screen!
- canShow = canShow || (wakingUp || willWakeUp || fullyAwake)
- && statusBarStateController.state == StatusBarState.KEYGUARD
+ canShow = canShow || (wakingUp || willWakeUp || fullyAwake) &&
+ statusBarStateController.state == StatusBarState.KEYGUARD
// We want to hide the notifications when collapsed too much
if (collapsedEnoughToHide) {
canShow = false
@@ -143,7 +141,7 @@
}
init {
- mHeadsUpManagerPhone.addListener(this)
+ mHeadsUpManager.addListener(this)
statusBarStateController.addCallback(this)
addListener(object : WakeUpListener {
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
@@ -155,7 +153,7 @@
increaseSpeed = false)
}
}
- });
+ })
}
fun setStackScroller(stackScroller: NotificationStackScrollLayout) {
@@ -178,46 +176,55 @@
* @param animate should this change be animated
* @param increaseSpeed should the speed be increased of the animation
*/
- fun setNotificationsVisibleForExpansion(visible: Boolean, animate: Boolean,
- increaseSpeed: Boolean) {
+ fun setNotificationsVisibleForExpansion(
+ visible: Boolean,
+ animate: Boolean,
+ increaseSpeed: Boolean
+ ) {
mNotificationsVisibleForExpansion = visible
updateNotificationVisibility(animate, increaseSpeed)
if (!visible && mNotificationsVisible) {
// If we stopped expanding and we're still visible because we had a pulse that hasn't
// times out, let's release them all to make sure were not stuck in a state where
// notifications are visible
- mHeadsUpManagerPhone.releaseAllImmediately()
+ mHeadsUpManager.releaseAllImmediately()
}
}
fun addListener(listener: WakeUpListener) {
- wakeUpListeners.add(listener);
+ wakeUpListeners.add(listener)
}
fun removeListener(listener: WakeUpListener) {
- wakeUpListeners.remove(listener);
+ wakeUpListeners.remove(listener)
}
- private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
+ private fun updateNotificationVisibility(
+ animate: Boolean,
+ increaseSpeed: Boolean
+ ) {
// TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
- var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications()
+ var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications()
visible = visible && canShowPulsingHuns
if (!visible && mNotificationsVisible && (wakingUp || willWakeUp) && mDozeAmount != 0.0f) {
// let's not make notifications invisible while waking up, otherwise the animation
// is strange
- return;
+ return
}
setNotificationsVisible(visible, animate, increaseSpeed)
}
- private fun setNotificationsVisible(visible: Boolean, animate: Boolean,
- increaseSpeed: Boolean) {
+ private fun setNotificationsVisible(
+ visible: Boolean,
+ animate: Boolean,
+ increaseSpeed: Boolean
+ ) {
if (mNotificationsVisible == visible) {
return
}
mNotificationsVisible = visible
- mVisibilityAnimator?.cancel();
+ mVisibilityAnimator?.cancel()
if (animate) {
notifyAnimationStart(visible)
startVisibilityAnimation(increaseSpeed)
@@ -230,8 +237,8 @@
if (updateDozeAmountIfBypass()) {
return
}
- if (linear != 1.0f && linear != 0.0f
- && (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
+ if (linear != 1.0f && linear != 0.0f &&
+ (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
// Let's notify the scroller that an animation started
notifyAnimationStart(mLinearDozeAmount == 1.0f)
}
@@ -245,17 +252,17 @@
mStackScroller.setDozeAmount(mDozeAmount)
updateHideAmount()
if (changed && linear == 0.0f) {
- setNotificationsVisible(visible = false, animate = false, increaseSpeed = false);
+ setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
setNotificationsVisibleForExpansion(visible = false, animate = false,
increaseSpeed = false)
}
}
override fun onStateChanged(newState: Int) {
- updateDozeAmountIfBypass();
+ updateDozeAmountIfBypass()
if (bypassController.bypassEnabled &&
- newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED
- && (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
+ newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED &&
+ (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
// We're leaving shade locked. Let's animate the notifications away
setNotificationsVisible(visible = true, increaseSpeed = false, animate = false)
setNotificationsVisible(visible = false, increaseSpeed = false, animate = true)
@@ -266,23 +273,23 @@
override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
val collapsedEnough = expansion <= 0.9f
if (collapsedEnough != this.collapsedEnoughToHide) {
- val couldShowPulsingHuns = canShowPulsingHuns;
+ val couldShowPulsingHuns = canShowPulsingHuns
this.collapsedEnoughToHide = collapsedEnough
if (couldShowPulsingHuns && !canShowPulsingHuns) {
updateNotificationVisibility(animate = true, increaseSpeed = true)
- mHeadsUpManagerPhone.releaseAllImmediately()
+ mHeadsUpManager.releaseAllImmediately()
}
}
}
private fun updateDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
- var amount = 1.0f;
- if (statusBarStateController.state == StatusBarState.SHADE
- || statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
- amount = 0.0f;
+ var amount = 1.0f
+ if (statusBarStateController.state == StatusBarState.SHADE ||
+ statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
+ amount = 0.0f
}
- setDozeAmount(amount, amount)
+ setDozeAmount(amount, amount)
return true
}
return false
@@ -300,7 +307,7 @@
visibilityAnimator.setInterpolator(Interpolators.LINEAR)
var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
if (increaseSpeed) {
- duration = (duration.toFloat() / 1.5F).toLong();
+ duration = (duration.toFloat() / 1.5F).toLong()
}
visibilityAnimator.setDuration(duration)
visibilityAnimator.start()
@@ -311,7 +318,7 @@
mLinearVisibilityAmount = visibilityAmount
mVisibilityAmount = mVisibilityInterpolator.getInterpolation(
visibilityAmount)
- handleAnimationFinished();
+ handleAnimationFinished()
updateHideAmount()
}
@@ -322,7 +329,7 @@
}
}
- fun getWakeUpHeight() : Float {
+ fun getWakeUpHeight(): Float {
return mStackScroller.wakeUpHeight
}
@@ -330,7 +337,7 @@
val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount)
val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount)
mStackScroller.setHideAmount(linearAmount, amount)
- notificationsFullyHidden = linearAmount == 1.0f;
+ notificationsFullyHidden = linearAmount == 1.0f
}
private fun notifyAnimationStart(awake: Boolean) {
@@ -361,7 +368,7 @@
// if we animate, we see the shelf briefly visible. Instead we fully animate
// the notification and its background out
animate = false
- } else if (!wakingUp && !willWakeUp){
+ } else if (!wakingUp && !willWakeUp) {
// TODO: look that this is done properly and not by anyone else
entry.setHeadsUpAnimatingAway(true)
mEntrySetToClearWhenFinished.add(entry)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index fabe3a7..b357ada 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -45,7 +45,6 @@
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Handler mHandler;
- private NotificationPresenter mPresenter;
private boolean mPanelExpanded;
private boolean mScreenOn;
private boolean mReorderingAllowed;
@@ -80,7 +79,6 @@
}
public void setUpWithPresenter(NotificationPresenter presenter) {
- mPresenter = presenter;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index b960b42..2c747bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -51,6 +52,7 @@
return mSummary;
}
+ @NonNull
public List<NotificationEntry> getChildren() {
return mUnmodifiableChildren;
}
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 749e5e2..b90cfa8 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
@@ -64,24 +64,34 @@
import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CleanUpEntryEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryAddedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryRemovedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryUpdatedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.InitEntryEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.notifcollection.RankingAppliedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.RankingUpdatedEvent;
import com.android.systemui.util.Assert;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Queue;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -124,6 +134,8 @@
private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
private final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
+ private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
+
private boolean mAttached = false;
private boolean mAmDispatchingToOtherCode;
@@ -160,8 +172,8 @@
mBuildListener = buildListener;
}
- /** @see NotifPipeline#getActiveNotifs() */
- Collection<NotificationEntry> getActiveNotifs() {
+ /** @see NotifPipeline#getAllNotifs() */
+ Collection<NotificationEntry> getAllNotifs() {
Assert.isMainThread();
return mReadOnlyNotificationSet;
}
@@ -242,7 +254,7 @@
}
locallyDismissNotifications(entriesToLocallyDismiss);
- rebuildList();
+ dispatchEventsAndRebuildList();
}
/**
@@ -251,8 +263,7 @@
public void dismissNotification(
NotificationEntry entry,
@NonNull DismissedByUserStats stats) {
- dismissNotifications(List.of(
- new Pair<NotificationEntry, DismissedByUserStats>(entry, stats)));
+ dismissNotifications(List.of(new Pair<>(entry, stats)));
}
/**
@@ -268,7 +279,7 @@
// system process is dead if we're here.
}
- final List<NotificationEntry> entries = new ArrayList(getActiveNotifs());
+ final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs());
for (int i = entries.size() - 1; i >= 0; i--) {
NotificationEntry entry = entries.get(i);
if (!shouldDismissOnClearAll(entry, userId)) {
@@ -283,7 +294,7 @@
}
locallyDismissNotifications(entries);
- rebuildList();
+ dispatchEventsAndRebuildList();
}
/**
@@ -326,8 +337,9 @@
private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
Assert.isMainThread();
- postNotification(sbn, requireRanking(rankingMap, sbn.getKey()), rankingMap);
- rebuildList();
+ postNotification(sbn, requireRanking(rankingMap, sbn.getKey()));
+ applyRanking(rankingMap);
+ dispatchEventsAndRebuildList();
}
private void onNotificationGroupPosted(List<CoalescedEvent> batch) {
@@ -336,9 +348,9 @@
mLogger.logNotifGroupPosted(batch.get(0).getSbn().getGroupKey(), batch.size());
for (CoalescedEvent event : batch) {
- postNotification(event.getSbn(), event.getRanking(), null);
+ postNotification(event.getSbn(), event.getRanking());
}
- rebuildList();
+ dispatchEventsAndRebuildList();
}
private void onNotificationRemoved(
@@ -354,55 +366,49 @@
throw new IllegalStateException("No notification to remove with key " + sbn.getKey());
}
entry.mCancellationReason = reason;
- applyRanking(rankingMap);
tryRemoveNotification(entry);
- rebuildList();
+ applyRanking(rankingMap);
+ dispatchEventsAndRebuildList();
}
private void onNotificationRankingUpdate(RankingMap rankingMap) {
Assert.isMainThread();
+ mEventQueue.add(new RankingUpdatedEvent(rankingMap));
applyRanking(rankingMap);
- dispatchNotificationRankingUpdate(rankingMap);
- rebuildList();
+ dispatchEventsAndRebuildList();
}
private void postNotification(
StatusBarNotification sbn,
- Ranking ranking,
- @Nullable RankingMap rankingMap) {
+ Ranking ranking) {
NotificationEntry entry = mNotificationSet.get(sbn.getKey());
if (entry == null) {
// A new notification!
- mLogger.logNotifPosted(sbn.getKey());
-
entry = new NotificationEntry(sbn, ranking);
mNotificationSet.put(sbn.getKey(), entry);
- dispatchOnEntryInit(entry);
- if (rankingMap != null) {
- applyRanking(rankingMap);
- }
-
- dispatchOnEntryAdded(entry);
+ mLogger.logNotifPosted(sbn.getKey());
+ mEventQueue.add(new InitEntryEvent(entry));
+ mEventQueue.add(new EntryAddedEvent(entry));
} else {
// Update to an existing entry
- mLogger.logNotifUpdated(sbn.getKey());
// Notification is updated so it is essentially re-added and thus alive again, so we
// can reset its state.
+ // TODO: If a coalesced event ever gets here, it's possible to lose track of children,
+ // since their rankings might have been updated earlier (and thus we may no longer
+ // think a child is associated with this locally-dismissed entry).
cancelLocalDismissal(entry);
cancelLifetimeExtension(entry);
cancelDismissInterception(entry);
entry.mCancellationReason = REASON_NOT_CANCELED;
entry.setSbn(sbn);
- if (rankingMap != null) {
- applyRanking(rankingMap);
- }
- dispatchOnEntryUpdated(entry);
+ mLogger.logNotifUpdated(sbn.getKey());
+ mEventQueue.add(new EntryUpdatedEvent(entry));
}
}
@@ -432,8 +438,8 @@
if (!isLifetimeExtended(entry)) {
mNotificationSet.remove(entry.getKey());
cancelDismissInterception(entry);
- dispatchOnEntryRemoved(entry, entry.mCancellationReason);
- dispatchOnEntryCleanUp(entry);
+ mEventQueue.add(new EntryRemovedEvent(entry, entry.mCancellationReason));
+ mEventQueue.add(new CleanUpEntryEvent(entry));
return true;
} else {
return false;
@@ -466,9 +472,16 @@
}
}
}
+ mEventQueue.add(new RankingAppliedEvent());
}
- private void rebuildList() {
+ private void dispatchEventsAndRebuildList() {
+ mAmDispatchingToOtherCode = true;
+ while (!mEventQueue.isEmpty()) {
+ mEventQueue.remove().dispatchTo(mNotifCollectionListeners);
+ }
+ mAmDispatchingToOtherCode = false;
+
if (mBuildListener != null) {
mBuildListener.onBuildList(mReadOnlyNotificationSet);
}
@@ -491,7 +504,7 @@
if (!isLifetimeExtended(entry)) {
if (tryRemoveNotification(entry)) {
- rebuildList();
+ dispatchEventsAndRebuildList();
}
}
}
@@ -660,57 +673,9 @@
|| entry.getSbn().getUser().getIdentifier() == userId;
}
- 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) {
- listener.onEntryAdded(entry);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchOnEntryUpdated(NotificationEntry entry) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryUpdated(entry);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchNotificationRankingUpdate(RankingMap map) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onRankingUpdate(map);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchOnEntryRemoved(NotificationEntry entry, @CancellationReason int reason) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryRemoved(entry, reason);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchOnEntryCleanUp(NotificationEntry entry) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryCleanUp(entry);
- }
- mAmDispatchingToOtherCode = false;
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
- final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
+ final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs());
pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
if (entries.size() == 0) {
@@ -754,6 +719,7 @@
private static final String TAG = "NotifCollection";
@IntDef(prefix = { "REASON_" }, value = {
+ REASON_NOT_CANCELED,
REASON_UNKNOWN,
REASON_CLICK,
REASON_CANCEL_ALL,
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 14903cd..17899e9 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
@@ -82,14 +82,14 @@
}
/**
- * Returns the list of "active" notifications, i.e. the notifications that are currently posted
+ * Returns the list of all known notifications, i.e. the notifications that are currently posted
* to the phone. In general, this tracks closely to the list maintained by NotificationManager,
* but it can diverge slightly due to lifetime extenders.
*
* The returned collection is read-only, unsorted, unfiltered, and ungrouped.
*/
- public Collection<NotificationEntry> getActiveNotifs() {
- return mNotifCollection.getActiveNotifs();
+ public Collection<NotificationEntry> getAllNotifs() {
+ return mNotifCollection.getAllNotifs();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt
new file mode 100644
index 0000000..e7948cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.collection
+
+import android.view.textclassifier.Log
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+import java.lang.IllegalStateException
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * The ViewBarn is just a map from [ListEntry] to an instance of [NotificationListItem] which is
+ * usually just an [ExpandableNotificationRow]
+ */
+@Singleton
+class NotifViewBarn @Inject constructor() {
+ private val DEBUG = false
+
+ private val rowMap = mutableMapOf<String, NotificationListItem>()
+
+ fun requireView(forEntry: ListEntry): NotificationListItem {
+ if (DEBUG) {
+ Log.d(TAG, "requireView: $forEntry.key")
+ }
+ val li = rowMap[forEntry.key]
+ if (li == null) {
+ throw IllegalStateException("No view has been registered for entry: $forEntry")
+ }
+
+ return li
+ }
+
+ fun registerViewForEntry(entry: ListEntry, view: NotificationListItem) {
+ if (DEBUG) {
+ Log.d(TAG, "registerViewForEntry: $entry.key")
+ }
+ rowMap[entry.key] = view
+ }
+
+ fun removeViewForEntry(entry: ListEntry) {
+ if (DEBUG) {
+ Log.d(TAG, "removeViewForEntry: $entry.key")
+ }
+ rowMap.remove(entry.key)
+ }
+}
+
+private const val TAG = "NotifViewBarn"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
new file mode 100644
index 0000000..0437877
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
@@ -0,0 +1,200 @@
+/*
+ * 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.statusbar.notification.collection
+
+import android.annotation.MainThread
+import android.view.ViewGroup
+
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.notification.VisualStabilityManager
+import com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+import com.android.systemui.util.Assert
+
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.IllegalStateException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * A consumer of a Notification tree built by [ShadeListBuilder] which will update the notification
+ * presenter with the minimum operations required to make the old tree match the new one
+ */
+@MainThread
+@Singleton
+class NotifViewManager @Inject constructor(
+ private val rowRegistry: NotifViewBarn,
+ private val stabilityManager: VisualStabilityManager,
+ private val featureFlags: FeatureFlags
+) {
+ var currentNotifs = listOf<ListEntry>()
+
+ private lateinit var listContainer: SimpleNotificationListContainer
+
+ fun attach(listBuilder: ShadeListBuilder) {
+ if (featureFlags.isNewNotifPipelineRenderingEnabled) {
+ listBuilder.setOnRenderListListener { entries: List<ListEntry> ->
+ this.onNotifTreeBuilt(entries)
+ }
+ }
+ }
+
+ fun setViewConsumer(consumer: SimpleNotificationListContainer) {
+ listContainer = consumer
+ }
+
+ /**
+ * Callback for when the tree is rebuilt
+ */
+ fun onNotifTreeBuilt(notifList: List<ListEntry>) {
+ Assert.isMainThread()
+
+ /*
+ * The assumption here is that anything from the old NotificationViewHierarchyManager that
+ * is responsible for filtering is done via the NotifFilter logic. This tree we get should
+ * be *the stuff to display* +/- redacted stuff
+ */
+
+ detachRows(notifList)
+ attachRows(notifList)
+
+ currentNotifs = notifList
+ }
+
+ private fun detachRows(entries: List<ListEntry>) {
+ // To properly detach rows, we are looking to remove any view in the consumer that is not
+ // present in the incoming list.
+ //
+ // Every listItem was top-level, so it's entry's parent was ROOT_ENTRY, but now
+ // there are two possibilities:
+ //
+ // 1. It is not present in the entry list
+ // 1a. It has moved to be a child in the entry list - transfer it
+ // 1b. It is gone completely - remove it
+ // 2. It is present in the entry list - diff the children
+ getListItems(listContainer)
+ .filter {
+ // Ignore things that are showing the blocking helper
+ !it.isBlockingHelperShowing
+ }
+ .forEach { listItem ->
+ val noLongerTopLevel = listItem.entry.parent != ROOT_ENTRY
+ val becameChild = noLongerTopLevel && listItem.entry.parent != null
+
+ val idx = entries.indexOf(listItem.entry)
+
+ if (noLongerTopLevel) {
+ // Summaries won't become children; remove the whole group
+ if (listItem.isSummaryWithChildren) {
+ listItem.removeAllChildren()
+ }
+
+ if (becameChild) {
+ // Top-level element is becoming a child, don't generate an animation
+ listContainer.setChildTransferInProgress(true)
+ }
+ listContainer.removeListItem(listItem)
+ listContainer.setChildTransferInProgress(false)
+ } else if (entries[idx] is GroupEntry) {
+ // A top-level entry exists. If it's a group, diff the children
+ val groupChildren = (entries[idx] as GroupEntry).children
+ listItem.notificationChildren?.forEach { listChild ->
+ if (!groupChildren.contains(listChild.entry)) {
+ listItem.removeChildNotification(listChild)
+
+ // TODO: the old code only calls this if the notif is gone from
+ // NEM.getActiveNotificationUnfiltered(). Do we care?
+ listContainer.notifyGroupChildRemoved(
+ listChild.view, listChild.view.parent as ViewGroup)
+ }
+ }
+ }
+ }
+ }
+
+ /** Convenience method for getting a sequence of [NotificationListItem]s */
+ private fun getListItems(container: SimpleNotificationListContainer):
+ Sequence<NotificationListItem> {
+ return (0 until container.getContainerChildCount()).asSequence()
+ .map { container.getContainerChildAt(it) }
+ .filterIsInstance<NotificationListItem>()
+ }
+
+ private fun attachRows(entries: List<ListEntry>) {
+
+ var orderChanged = false
+
+ // To attach rows we can use _this one weird trick_: if the intended view to add does not
+ // have a parent, then simply add it (and its children).
+ entries.forEach { entry ->
+ val listItem = rowRegistry.requireView(entry)
+
+ if (listItem.view.parent != null) {
+ listContainer.addListItem(listItem)
+ stabilityManager.notifyViewAddition(listItem.view)
+ }
+
+ if (entry is GroupEntry) {
+ for ((idx, childEntry) in entry.children.withIndex()) {
+ val childListItem = rowRegistry.requireView(childEntry)
+ // Child hasn't been added yet. add it!
+ if (!listItem.notificationChildren.contains(childListItem)) {
+ // TODO: old code here just Log.wtf()'d here. This might wreak havoc
+ if (childListItem.view.parent != null) {
+ throw IllegalStateException("trying to add a notification child that " +
+ "already has a parent. class: " +
+ "${childListItem.view.parent?.javaClass} " +
+ "\n child: ${childListItem.view}"
+ )
+ }
+
+ listItem.addChildNotification(childListItem, idx)
+ stabilityManager.notifyViewAddition(childListItem.view)
+ listContainer.notifyGroupChildAdded(childListItem.view)
+ }
+ }
+
+ // finally after removing and adding has been performed we can apply the order
+ orderChanged = orderChanged ||
+ listItem.applyChildOrder(
+ getChildListFromParent(entry),
+ stabilityManager,
+ null /*TODO: stability callback */
+ )
+ }
+ }
+
+ if (orderChanged) {
+ listContainer.generateChildOrderChangedEvent()
+ }
+ }
+
+ private fun getChildListFromParent(parent: ListEntry): List<NotificationListItem> {
+ if (parent is GroupEntry) {
+ return parent.children.map { child -> rowRegistry.requireView(child) }
+ .toList()
+ }
+
+ return emptyList()
+ }
+
+ fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+ }
+}
+
+private const val TAG = "NotifViewDataSource"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 3e9d8a4..7019b5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -29,8 +29,6 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
@@ -44,10 +42,7 @@
import android.app.Person;
import android.app.RemoteInputHistoryItem;
import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.ShortcutInfo;
-import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
@@ -55,25 +50,20 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -81,8 +71,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -102,15 +90,11 @@
* clean this up in the future.
*/
public final class NotificationEntry extends ListEntry {
- private static final String TAG = "NotificationEntry";
private final String mKey;
private StatusBarNotification mSbn;
private Ranking mRanking;
- private StatusBarIcon mSmallIcon;
- private StatusBarIcon mPeopleAvatar;
-
/*
* Bookkeeping members
*/
@@ -142,10 +126,7 @@
* TODO: Remove every member beneath this line if possible
*/
- public StatusBarIconView icon;
- public StatusBarIconView expandedIcon;
- public StatusBarIconView centeredIcon;
- public StatusBarIconView aodIcon;
+ private IconPack mIcons = IconPack.buildEmptyPack(null);
private boolean interruption;
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
@@ -191,7 +172,8 @@
private boolean hasSentReply;
private boolean mSensitive = true;
- private Runnable mOnSensitiveChangedListener;
+ private List<OnSensitivityChangedListener> mOnSensitivityChangedListeners = new ArrayList<>();
+
private boolean mAutoHeadsUp;
private boolean mPulseSupressed;
private boolean mAllowFgsDismissal;
@@ -347,6 +329,15 @@
* TODO: Remove as many of these as possible
*/
+ @NonNull
+ public IconPack getIcons() {
+ return mIcons;
+ }
+
+ public void setIcons(@NonNull IconPack icons) {
+ mIcons = icons;
+ }
+
public void setInterruption() {
interruption = true;
}
@@ -464,239 +455,6 @@
|| SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
}
- /**
- * Create the icons for a notification
- * @param context the context to create the icons with
- * @param sbn the notification
- * @throws InflationException Exception if required icons are not valid or specified
- */
- public void createIcons(Context context, StatusBarNotification sbn)
- throws InflationException {
- StatusBarIcon ic = getIcon(context, sbn, false /* redact */);
-
- // Construct the icon.
- icon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
- // Construct the expanded icon.
- expandedIcon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
- // Construct the expanded icon.
- aodIcon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- aodIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- aodIcon.setIncreasedSize(true);
-
- try {
- setIcons(ic, Collections.singletonList(icon));
- if (isSensitive()) {
- ic = getIcon(context, sbn, true /* redact */);
- }
- setIcons(ic, Arrays.asList(expandedIcon, aodIcon));
- } catch (InflationException e) {
- icon = null;
- expandedIcon = null;
- centeredIcon = null;
- aodIcon = null;
- throw e;
- }
-
- expandedIcon.setVisibility(View.INVISIBLE);
- expandedIcon.setOnVisibilityChangedListener(
- newVisibility -> {
- if (row != null) {
- row.setIconsVisible(newVisibility != View.VISIBLE);
- }
- });
-
- // Construct the centered icon
- if (mSbn.getNotification().isMediaNotification()) {
- centeredIcon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- centeredIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- try {
- setIcons(ic, Collections.singletonList(centeredIcon));
- } catch (InflationException e) {
- centeredIcon = null;
- throw e;
- }
- }
- }
-
- /**
- * Determines if this icon should be tinted based on the sensitivity of the icon, its context
- * and the user's indicated sensitivity preference.
- *
- * @param ic The icon that should/should not be tinted.
- * @return
- */
- private boolean shouldTintIcon(StatusBarIconView ic) {
- boolean usedInSensitiveContext = (ic == expandedIcon || ic == aodIcon);
- return !isImportantConversation() || (usedInSensitiveContext && isSensitive());
- }
-
-
- private void setIcons(StatusBarIcon ic, List<StatusBarIconView> icons)
- throws InflationException {
- for (StatusBarIconView icon: icons) {
- if (icon == null) {
- continue;
- }
- icon.setTintIcons(shouldTintIcon(icon));
- if (!icon.set(ic)) {
- throw new InflationException("Couldn't create icon" + ic);
- }
- }
- }
-
- private StatusBarIcon getIcon(Context context, StatusBarNotification sbn, boolean redact)
- throws InflationException {
- Notification n = sbn.getNotification();
- final boolean showPeopleAvatar = isImportantConversation() && !redact;
-
- // If cached, return corresponding cached values
- if (showPeopleAvatar && mPeopleAvatar != null) {
- return mPeopleAvatar;
- } else if (!showPeopleAvatar && mSmallIcon != null) {
- return mSmallIcon;
- }
-
- Icon icon = showPeopleAvatar ? createPeopleAvatar(context) : n.getSmallIcon();
- if (icon == null) {
- throw new InflationException("No icon in notification from " + sbn.getPackageName());
- }
-
- StatusBarIcon ic = new StatusBarIcon(
- sbn.getUser(),
- sbn.getPackageName(),
- icon,
- n.iconLevel,
- n.number,
- StatusBarIconView.contentDescForNotification(context, n));
-
- // Cache if important conversation.
- if (isImportantConversation()) {
- if (showPeopleAvatar) {
- mPeopleAvatar = ic;
- } else {
- mSmallIcon = ic;
- }
- }
- return ic;
- }
-
- private Icon createPeopleAvatar(Context context) throws InflationException {
- // Attempt to extract form shortcut.
- String conversationId = getChannel().getConversationId();
- ShortcutQuery query = new ShortcutQuery()
- .setPackage(mSbn.getPackageName())
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Collections.singletonList(conversationId));
- List<ShortcutInfo> shortcuts = context.getSystemService(LauncherApps.class)
- .getShortcuts(query, mSbn.getUser());
- Icon ic = null;
- if (shortcuts != null && !shortcuts.isEmpty()) {
- ic = shortcuts.get(0).getIcon();
- }
-
- // Fall back to notification large icon if available
- if (ic == null) {
- ic = mSbn.getNotification().getLargeIcon();
- }
-
- // Fall back to extract from message
- if (ic == null) {
- Bundle extras = mSbn.getNotification().extras;
- List<Message> messages = Message.getMessagesFromBundleArray(
- extras.getParcelableArray(Notification.EXTRA_MESSAGES));
- Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
-
- for (int i = messages.size() - 1; i >= 0; i--) {
- Message message = messages.get(i);
- Person sender = message.getSenderPerson();
- if (sender != null && sender != user) {
- ic = message.getSenderPerson().getIcon();
- break;
- }
- }
- }
-
- // Revert to small icon if still not available
- if (ic == null) {
- ic = mSbn.getNotification().getSmallIcon();
- }
- if (ic == null) {
- throw new InflationException("No icon in notification from " + mSbn.getPackageName());
- }
- return ic;
- }
-
- private void updateSensitiveIconState() {
- try {
- StatusBarIcon ic = getIcon(getRow().getContext(), mSbn, isSensitive());
- setIcons(ic, Arrays.asList(expandedIcon, aodIcon));
- } catch (InflationException e) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Unable to update icon", e);
- }
- }
- }
-
- public void setIconTag(int key, Object tag) {
- if (icon != null) {
- icon.setTag(key, tag);
- expandedIcon.setTag(key, tag);
- }
-
- if (centeredIcon != null) {
- centeredIcon.setTag(key, tag);
- }
-
- if (aodIcon != null) {
- aodIcon.setTag(key, tag);
- }
- }
-
- /**
- * Update the notification icons.
- *
- * @param context the context to create the icons with.
- * @param sbn the notification to read the icon from.
- * @throws InflationException Exception if required icons are not valid or specified
- */
- public void updateIcons(Context context, StatusBarNotification sbn)
- throws InflationException {
- if (icon != null) {
- // Update the icon
- mSmallIcon = null;
- mPeopleAvatar = null;
-
- StatusBarIcon ic = getIcon(context, sbn, false /* redact */);
-
- icon.setNotification(sbn);
- expandedIcon.setNotification(sbn);
- aodIcon.setNotification(sbn);
- setIcons(ic, Arrays.asList(icon, expandedIcon));
-
- if (isSensitive()) {
- ic = getIcon(context, sbn, true /* redact */);
- }
- setIcons(ic, Collections.singletonList(aodIcon));
-
- if (centeredIcon != null) {
- centeredIcon.setNotification(sbn);
- setIcons(ic, Collections.singletonList(centeredIcon));
- }
- }
- }
-
- private boolean isImportantConversation() {
- return getChannel() != null && getChannel().isImportantConversation();
- }
-
public int getContrastedColor(Context context, boolean isLowPriority,
int backgroundColor) {
int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
@@ -1125,9 +883,8 @@
getRow().setSensitive(sensitive, deviceSensitive);
if (sensitive != mSensitive) {
mSensitive = sensitive;
- updateSensitiveIconState();
- if (mOnSensitiveChangedListener != null) {
- mOnSensitiveChangedListener.run();
+ for (int i = 0; i < mOnSensitivityChangedListeners.size(); i++) {
+ mOnSensitivityChangedListeners.get(i).onSensitivityChanged(this);
}
}
}
@@ -1136,8 +893,14 @@
return mSensitive;
}
- public void setOnSensitiveChangedListener(Runnable listener) {
- mOnSensitiveChangedListener = listener;
+ /** Add a listener to be notified when the entry's sensitivity changes. */
+ public void addOnSensitivityChangedListener(OnSensitivityChangedListener listener) {
+ mOnSensitivityChangedListeners.add(listener);
+ }
+
+ /** Remove a listener that was registered above. */
+ public void removeOnSensitivityChangedListener(OnSensitivityChangedListener listener) {
+ mOnSensitivityChangedListeners.remove(listener);
}
public boolean isPulseSuppressed() {
@@ -1167,6 +930,12 @@
}
}
+ /** Listener interface for {@link #addOnSensitivityChangedListener} */
+ public interface OnSensitivityChangedListener {
+ /** Called when the sensitivity changes */
+ void onSensitivityChanged(@NonNull NotificationEntry entry);
+ }
+
/** @see #getDismissState() */
public enum DismissState {
/** User has not dismissed this notif or its parent */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 5b73b1a..f7d6cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -319,7 +319,7 @@
logParentingChanges();
freeEmptyGroups();
- // Step 6: Dispatch the new list, first to any listeners and then to the view layer
+ // Step 8: Dispatch the new list, first to any listeners and then to the view layer
if (mIterationCount % 10 == 0) {
mLogger.logFinalList(mNotifList);
}
@@ -328,7 +328,7 @@
mOnRenderListListener.onRenderList(mReadOnlyNotifList);
}
- // Step 7: We're done!
+ // Step 9: We're done!
mLogger.logEndBuildList(mIterationCount);
mPipelineState.setState(STATE_IDLE);
mIterationCount++;
@@ -816,7 +816,7 @@
* @param entries A read-only view into the current notif list. Note that this list is
* backed by the live list and will change in response to new pipeline runs.
*/
- void onRenderList(List<ListEntry> entries);
+ void onRenderList(@NonNull List<ListEntry> entries);
}
private static final NotifSection sDefaultSection =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt
new file mode 100644
index 0000000..2dbe555
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.collection
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+
+/**
+ * Minimal interface of what [NotifViewManager] needs from [NotificationListContainer]
+ */
+interface SimpleNotificationListContainer {
+ /** Called to signify that a top-level element is becoming a child in the shade */
+ fun setChildTransferInProgress(b: Boolean)
+ /** Used to generate a list of [NotificationListItem] */
+ fun getContainerChildAt(i: Int): View
+ /** Similar to above */
+ fun getContainerChildCount(): Int
+ /** Remove a [NotificationListItem] from the container */
+ fun removeListItem(li: NotificationListItem)
+ /** Add a [NotificationListItem] to the container */
+ fun addListItem(li: NotificationListItem)
+ /** Allows [NotifViewManager] to notify the container about a group child removal */
+ fun notifyGroupChildRemoved(row: View, parent: ViewGroup)
+ /** Allows [NotifViewManager] to notify the container about a group child addition */
+ fun notifyGroupChildAdded(row: View)
+ /** [NotifViewManager] calls this when the order of the children changes */
+ fun generateChildOrderChangedEvent()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 98c45ff..596235c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -260,11 +260,14 @@
private void applyRanking(RankingMap rankingMap) {
for (CoalescedEvent event : mCoalescedEvents.values()) {
Ranking ranking = new Ranking();
- if (!rankingMap.getRanking(event.getKey(), ranking)) {
- throw new IllegalStateException(
- "Ranking map doesn't contain key: " + event.getKey());
+ if (rankingMap.getRanking(event.getKey(), ranking)) {
+ event.setRanking(ranking);
+ } else {
+ // TODO: (b/148791039) We should crash if we are ever handed a ranking with
+ // incomplete entries. Right now, there's a race condition in NotificationListener
+ // that means this might occur when SystemUI is starting up.
+ mLogger.logMissingRanking(event.getKey());
}
- event.setRanking(ranking);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
index 6e8788d..d4d5b64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -57,6 +57,14 @@
"Modification of notif $str1 triggered TIMEOUT emit of batched group $str2"
})
}
+
+ fun logMissingRanking(forKey: String) {
+ buffer.log(TAG, LogLevel.WARNING, {
+ str1 = forKey
+ }, {
+ "RankingMap is missing an entry for coalesced notification $str1"
+ })
+ }
}
private const val TAG = "GroupCoalescer"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 370de83..92426e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -126,7 +126,7 @@
mInterceptedDismissalEntries.remove(entry.getKey());
mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
createDismissedByUserStats(entry));
- } else if (mNotifPipeline.getActiveNotifs().contains(entry)) {
+ } else if (mNotifPipeline.getAllNotifs().contains(entry)) {
// Bubbles are hiding the notifications from the shade, but the bubble was
// deleted; therefore, the notification should be cancelled as if it were a user
// dismissal (this won't re-enter handleInterceptDimissal because Bubbles
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
index 854444f..b5b756d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -240,7 +240,7 @@
}
private NotificationEntry findNotificationEntryWithKey(String key) {
- for (NotificationEntry entry : mNotifPipeline.getActiveNotifs()) {
+ for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) {
if (entry.getKey().equals(key)) {
return entry;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
new file mode 100644
index 0000000..573c129
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -0,0 +1,171 @@
+/*
+ * 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.collection.coordinator;
+
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
+
+import android.annotation.Nullable;
+
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.util.Objects;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Coordinates heads up notification (HUN) interactions with the notification pipeline based on
+ * the HUN state reported by the {@link HeadsUpManager}. In this class we only consider one
+ * notification, in particular the {@link HeadsUpManager#getTopEntry()}, to be HeadsUpping at a
+ * time even though other notifications may be queued to heads up next.
+ *
+ * The current HUN, but not HUNs that are queued to heads up, will be:
+ * - Lifetime extended until it's no longer heads upping.
+ * - Promoted out of its group if it's a child of a group.
+ * - In the HeadsUpCoordinatorSection. Ordering is configured in {@link NotifCoordinators}.
+ * - Removed from HeadsUpManager if it's removed from the NotificationCollection.
+ *
+ * Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs.
+ */
+@Singleton
+public class HeadsUpCoordinator implements Coordinator {
+ private static final String TAG = "HeadsUpCoordinator";
+
+ private final HeadsUpManager mHeadsUpManager;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+
+ // tracks the current HeadUpNotification reported by HeadsUpManager
+ private @Nullable NotificationEntry mCurrentHun;
+
+ private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
+ private NotificationEntry mNotifExtendingLifetime; // notif we've extended the lifetime for
+
+ @Inject
+ public HeadsUpCoordinator(
+ HeadsUpManager headsUpManager,
+ NotificationRemoteInputManager remoteInputManager) {
+ mHeadsUpManager = headsUpManager;
+ mRemoteInputManager = remoteInputManager;
+ }
+
+ @Override
+ public void attach(NotifPipeline pipeline) {
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
+ pipeline.addCollectionListener(mNotifCollectionListener);
+ pipeline.addPromoter(mNotifPromoter);
+ pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
+ }
+
+ @Override
+ public NotifSection getSection() {
+ return mNotifSection;
+ }
+
+ private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
+ /**
+ * Stop alerting HUNs that are removed from the notification collection
+ */
+ @Override
+ public void onEntryRemoved(NotificationEntry entry, int reason) {
+ final String entryKey = entry.getKey();
+ if (mHeadsUpManager.isAlerting(entryKey)) {
+ boolean removeImmediatelyForRemoteInput =
+ mRemoteInputManager.getController().isSpinning(entryKey)
+ && !FORCE_REMOTE_INPUT_HISTORY;
+ mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
+ }
+ }
+ };
+
+ private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public void setCallback(OnEndLifetimeExtensionCallback callback) {
+ mEndLifetimeExtension = callback;
+ }
+
+ @Override
+ public boolean shouldExtendLifetime(NotificationEntry entry, int reason) {
+ boolean isShowingHun = isCurrentlyShowingHun(entry);
+ if (isShowingHun) {
+ mNotifExtendingLifetime = entry;
+ }
+ return isShowingHun;
+ }
+
+ @Override
+ public void cancelLifetimeExtension(NotificationEntry entry) {
+ if (Objects.equals(mNotifExtendingLifetime, entry)) {
+ mNotifExtendingLifetime = null;
+ }
+ }
+ };
+
+ private final NotifPromoter mNotifPromoter = new NotifPromoter(TAG) {
+ @Override
+ public boolean shouldPromoteToTopLevel(NotificationEntry entry) {
+ return isCurrentlyShowingHun(entry);
+ }
+ };
+
+ private final NotifSection mNotifSection = new NotifSection(TAG) {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ return isCurrentlyShowingHun(entry);
+ }
+ };
+
+ private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
+ new OnHeadsUpChangedListener() {
+ @Override
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ NotificationEntry newHUN = mHeadsUpManager.getTopEntry();
+ if (!Objects.equals(mCurrentHun, newHUN)) {
+ endNotifLifetimeExtension();
+ mCurrentHun = newHUN;
+ mNotifPromoter.invalidateList();
+ mNotifSection.invalidateList();
+ }
+ }
+ };
+
+ private boolean isCurrentlyShowingHun(ListEntry entry) {
+ return mCurrentHun == entry.getRepresentativeEntry();
+ }
+
+ private void endNotifLifetimeExtension() {
+ if (mNotifExtendingLifetime != null) {
+ mEndLifetimeExtension.onEndLifetimeExtension(
+ mLifetimeExtender,
+ mNotifExtendingLifetime);
+ mNotifExtendingLifetime = null;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 7a22d75..98104f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -49,6 +49,7 @@
public NotifCoordinators(
DumpManager dumpManager,
FeatureFlags featureFlags,
+ HeadsUpCoordinator headsUpCoordinator,
KeyguardCoordinator keyguardCoordinator,
RankingCoordinator rankingCoordinator,
ForegroundCoordinator foregroundCoordinator,
@@ -56,7 +57,6 @@
BubbleCoordinator bubbleCoordinator,
PreparationCoordinator preparationCoordinator) {
dumpManager.registerDumpable(TAG, this);
-
mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
mCoordinators.add(keyguardCoordinator);
mCoordinators.add(rankingCoordinator);
@@ -64,9 +64,10 @@
mCoordinators.add(deviceProvisionedCoordinator);
mCoordinators.add(bubbleCoordinator);
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mCoordinators.add(headsUpCoordinator);
mCoordinators.add(preparationCoordinator);
}
- // TODO: add new Coordinators here! (b/145134683, b/112656837)
+ // TODO: add new Coordinators here! (b/112656837)
// TODO: add the sections in a particular ORDER (HeadsUp < People < Alerting)
for (Coordinator c : mCoordinators) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index ebecf18..742615c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -26,13 +26,16 @@
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewBarn;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -57,21 +60,31 @@
private final PreparationCoordinatorLogger mLogger;
private final NotifInflater mNotifInflater;
private final NotifInflationErrorManager mNotifErrorManager;
+ private final NotifViewBarn mViewBarn;
private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
private final IStatusBarService mStatusBarService;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final HeadsUpManager mHeadsUpManager;
@Inject
public PreparationCoordinator(
PreparationCoordinatorLogger logger,
NotifInflaterImpl notifInflater,
NotifInflationErrorManager errorManager,
- IStatusBarService service) {
+ NotifViewBarn viewBarn,
+ IStatusBarService service,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
+ HeadsUpManager headsUpManager
+ ) {
mLogger = logger;
mNotifInflater = notifInflater;
mNotifInflater.setInflationCallback(mInflationCallback);
mNotifErrorManager = errorManager;
mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
+ mViewBarn = viewBarn;
mStatusBarService = service;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+ mHeadsUpManager = headsUpManager;
}
@Override
@@ -109,6 +122,7 @@
@Override
public void onEntryCleanUp(NotificationEntry entry) {
mInflationStates.remove(entry);
+ mViewBarn.removeViewForEntry(entry);
}
};
@@ -142,7 +156,13 @@
@Override
public void onInflationFinished(NotificationEntry entry) {
mLogger.logNotifInflated(entry.getKey());
+ mViewBarn.registerViewForEntry(entry, entry.getRow());
mInflationStates.put(entry, STATE_INFLATED);
+
+ // TODO: should eventually be moved to HeadsUpCoordinator
+ if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
+ mHeadsUpManager.showNotification(entry);
+ }
mNotifInflatingFilter.invalidateList();
}
};
@@ -151,6 +171,7 @@
new NotifInflationErrorManager.NotifInflationErrorListener() {
@Override
public void onNotifInflationError(NotificationEntry entry, Exception e) {
+ mViewBarn.removeViewForEntry(entry);
mInflationStates.put(entry, STATE_ERROR);
try {
final StatusBarNotification sbn = entry.getSbn();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e8a62e4..7237284 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
@@ -29,34 +28,29 @@
import android.view.ViewGroup;
import com.android.internal.util.NotificationMessagingUtil;
-import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationClicker;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.icon.IconManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import java.util.Objects;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
@@ -66,23 +60,23 @@
private static final String TAG = "NotificationViewManager";
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
-
private final Context mContext;
- private final NotifBindPipeline mNotifBindPipeline;
- private final RowContentBindStage mRowContentBindStage;
private final NotificationMessagingUtil mMessagingUtil;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ private final NotifBindPipeline mNotifBindPipeline;
+ private final RowContentBindStage mRowContentBindStage;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
+ private final ExpandableNotificationRowComponent.Builder
+ mExpandableNotificationRowComponentBuilder;
+ private final IconManager mIconManager;
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
private NotificationRowContentBinder.InflationCallback mInflationCallback;
private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
- private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
- private final ExpandableNotificationRowComponent.Builder
- mExpandableNotificationRowComponentBuilder;
@Inject
public NotificationRowBinderImpl(
@@ -92,23 +86,20 @@
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotifBindPipeline notifBindPipeline,
RowContentBindStage rowContentBindStage,
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
- KeyguardBypassController keyguardBypassController,
- StatusBarStateController statusBarStateController,
- NotificationGroupManager notificationGroupManager,
- NotificationGutsManager notificationGutsManager,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptionStateProvider,
Provider<RowInflaterTask> rowInflaterTaskProvider,
- ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) {
+ ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
+ IconManager iconManager) {
mContext = context;
mNotifBindPipeline = notifBindPipeline;
mRowContentBindStage = rowContentBindStage;
mMessagingUtil = notificationMessagingUtil;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationLockscreenUserManager = notificationLockscreenUserManager;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
mRowInflaterTaskProvider = rowInflaterTaskProvider;
mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
+ mIconManager = iconManager;
}
/**
@@ -120,6 +111,8 @@
mPresenter = presenter;
mListContainer = listContainer;
mBindRowCallback = bindRowCallback;
+
+ mIconManager.attach();
}
public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
@@ -142,12 +135,12 @@
final StatusBarNotification sbn = entry.getSbn();
if (entry.rowExists()) {
- entry.updateIcons(mContext, sbn);
+ mIconManager.updateIcons(entry);
entry.reset();
updateNotification(entry, pmUser, sbn, entry.getRow());
entry.getRowController().setOnDismissRunnable(onDismissRunnable);
} else {
- entry.createIcons(mContext, sbn);
+ mIconManager.createIcons(entry);
mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
row -> {
// Setup the controller for the view.
@@ -227,8 +220,8 @@
row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
&& entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- // TODO: should updates to the entry be happening somewhere else?
- entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+ // TODO: should this be happening somewhere else?
+ mIconManager.updateIconTags(entry, entry.targetSdk);
row.setOnActivatedListener(mPresenter);
@@ -243,7 +236,7 @@
params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
params.setUseLowPriority(entry.isAmbient());
- if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+ if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
}
//TODO: Replace this API with RowContentBindParams directly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
deleted file mode 100644
index 15f312d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
+++ /dev/null
@@ -1,85 +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.statusbar.notification.collection.init;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Temporary class that tracks the result of the list builder and dumps it to text when requested.
- *
- * Eventually, this will be something that hands off the result of the pipeline to the View layer.
- */
-public class FakePipelineConsumer implements Dumpable {
- private List<ListEntry> mEntries = Collections.emptyList();
-
- /** Attach the consumer to the pipeline. */
- public void attach(ShadeListBuilder listBuilder) {
- listBuilder.setOnRenderListListener(this::onBuildComplete);
- }
-
- private void onBuildComplete(List<ListEntry> entries) {
- mEntries = entries;
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println();
- pw.println("Active notif tree:");
- for (int i = 0; i < mEntries.size(); i++) {
- ListEntry entry = mEntries.get(i);
- if (entry instanceof GroupEntry) {
- GroupEntry ge = (GroupEntry) entry;
- pw.println(dumpGroup(ge, "", i));
-
- pw.println(dumpEntry(ge.getSummary(), INDENT, -1));
- for (int j = 0; j < ge.getChildren().size(); j++) {
- pw.println(dumpEntry(ge.getChildren().get(j), INDENT, j));
- }
- } else {
- pw.println(dumpEntry(entry.getRepresentativeEntry(), "", i));
- }
- }
- }
-
- private String dumpGroup(GroupEntry entry, String indent, int index) {
- return String.format(
- "%s[%d] %s (group)",
- indent,
- index,
- entry.getKey());
- }
-
- private String dumpEntry(NotificationEntry entry, String indent, int index) {
- return String.format(
- "%s[%s] %s (channel=%s)",
- indent,
- index == -1 ? "*" : Integer.toString(index),
- entry.getKey(),
- entry.getChannel() != null ? entry.getChannel().getId() : "");
- }
-
- private static final String INDENT = " ";
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 258f6d0..f150257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -25,10 +25,12 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewManager;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -50,7 +52,7 @@
private final DumpManager mDumpManager;
private final FeatureFlags mFeatureFlags;
- private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
+ private final NotifViewManager mNotifViewManager;
@Inject
public NotifPipelineInitializer(
@@ -61,7 +63,8 @@
NotifCoordinators notifCoordinators,
NotifInflaterImpl notifInflater,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ NotifViewManager notifViewManager) {
mPipelineWrapper = pipelineWrapper;
mGroupCoalescer = groupCoalescer;
mNotifCollection = notifCollection;
@@ -70,12 +73,14 @@
mDumpManager = dumpManager;
mNotifInflater = notifInflater;
mFeatureFlags = featureFlags;
+ mNotifViewManager = notifViewManager;
}
/** Hooks the new pipeline up to NotificationManager */
public void initialize(
NotificationListener notificationService,
- NotificationRowBinderImpl rowBinder) {
+ NotificationRowBinderImpl rowBinder,
+ NotificationListContainer listContainer) {
mDumpManager.registerDumpable("NotifPipeline", this);
@@ -88,7 +93,8 @@
mNotifPluggableCoordinators.attach(mPipelineWrapper);
// Wire up pipeline
- mFakePipelineConsumer.attach(mListBuilder);
+ mNotifViewManager.setViewConsumer(listContainer);
+ mNotifViewManager.attach(mListBuilder);
mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
mGroupCoalescer.attach(notificationService);
@@ -98,7 +104,7 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mFakePipelineConsumer.dump(fd, pw, args);
+ mNotifViewManager.dump(fd, pw, args);
mNotifPluggableCoordinators.dump(fd, pw, args);
mGroupCoalescer.dump(fd, pw, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
index 171816f..b4c2bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
@@ -20,6 +20,8 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import java.util.Collection;
+
/**
* A notification collection that manages the list of {@link NotificationEntry}s that will be
* rendered.
@@ -34,4 +36,13 @@
* or deleted.
*/
void addCollectionListener(NotifCollectionListener listener);
+
+ /**
+ * Returns the list of all known notifications, i.e. the notifications that are currently posted
+ * to the phone. In general, this tracks closely to the list maintained by NotificationManager,
+ * but it can diverge slightly due to lifetime extenders.
+ *
+ * The returned collection is read-only, unsorted, unfiltered, and ungrouped.
+ */
+ Collection<NotificationEntry> getAllNotifs();
}
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 b2c53da..0c0cded 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
@@ -70,8 +70,24 @@
}
/**
- * 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.
+ * Called whenever a ranking update is applied. During a ranking update, all active,
+ * non-lifetime-extended notification entries will have their ranking object updated.
+ *
+ * Ranking updates occur whenever a notification is added, updated, or removed, or when a
+ * standalone ranking is sent from the server.
+ */
+ default void onRankingApplied() {
+ }
+
+ /**
+ * Called whenever system server sends a standalone ranking update (i.e. one that isn't
+ * associated with a notification being added or removed).
+ *
+ * In general it is unsafe to depend on this method as rankings can change for other reasons.
+ * Instead, listen for {@link #onRankingApplied()}, which is called whenever ANY ranking update
+ * is applied, regardless of source.
+ *
+ * @deprecated Use {@link #onRankingApplied()} instead.
*/
default void onRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
new file mode 100644
index 0000000..2ef0368
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.collection.notifcollection
+
+import android.service.notification.NotificationListenerService.RankingMap
+import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * Set of classes that represent the various events that [NotifCollection] can dispatch to
+ * [NotifCollectionListener]s.
+ *
+ * These events build up in a queue and are periodically emitted in chunks by the collection.
+ */
+
+sealed class NotifEvent {
+ fun dispatchTo(listeners: List<NotifCollectionListener>) {
+ for (i in listeners.indices) {
+ dispatchToListener(listeners[i])
+ }
+ }
+
+ abstract fun dispatchToListener(listener: NotifCollectionListener)
+}
+
+data class InitEntryEvent(
+ val entry: NotificationEntry
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryInit(entry)
+ }
+}
+
+data class EntryAddedEvent(
+ val entry: NotificationEntry
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryAdded(entry)
+ }
+}
+
+data class EntryUpdatedEvent(
+ val entry: NotificationEntry
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryUpdated(entry)
+ }
+}
+
+data class EntryRemovedEvent(
+ val entry: NotificationEntry,
+ val reason: Int
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryRemoved(entry, reason)
+ }
+}
+
+data class CleanUpEntryEvent(
+ val entry: NotificationEntry
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryCleanUp(entry)
+ }
+}
+
+data class RankingUpdatedEvent(
+ val rankingMap: RankingMap
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onRankingUpdate(rankingMap)
+ }
+}
+
+class RankingAppliedEvent() : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onRankingApplied()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index e425ee9..1d8e979 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -31,10 +31,8 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
@@ -44,6 +42,9 @@
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
@@ -58,6 +59,7 @@
import javax.inject.Singleton;
+import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -127,7 +129,7 @@
NotificationRemoteInputManager remoteInputManager,
VisualStabilityManager visualStabilityManager,
StatusBarStateController statusBarStateController,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationListener notificationListener,
HeadsUpManager headsUpManager) {
return new NotificationAlertingManager(
@@ -135,7 +137,7 @@
remoteInputManager,
visualStabilityManager,
statusBarStateController,
- notificationInterruptionStateProvider,
+ notificationInterruptStateProvider,
notificationListener,
headsUpManager);
}
@@ -210,4 +212,9 @@
NotificationEntryManager entryManager) {
return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager;
}
+
+ /** */
+ @Binds
+ NotificationInterruptStateProvider bindNotificationInterruptStateProvider(
+ NotificationInterruptStateProviderImpl notificationInterruptStateProviderImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
new file mode 100644
index 0000000..afc123f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.icon
+
+import android.app.Notification
+import android.content.Context
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import javax.inject.Inject
+
+/**
+ * Testable wrapper around Context.
+ */
+class IconBuilder @Inject constructor(
+ private val context: Context
+) {
+ fun createIconView(entry: NotificationEntry): StatusBarIconView {
+ return StatusBarIconView(
+ context,
+ "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}",
+ entry.sbn)
+ }
+
+ fun getIconContentDescription(n: Notification): CharSequence {
+ return StatusBarIconView.contentDescForNotification(context, n)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
new file mode 100644
index 0000000..bb0fcaf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -0,0 +1,338 @@
+/*
+ * 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.icon
+
+import android.app.Notification
+import android.app.Person
+import android.content.pm.LauncherApps
+import android.graphics.drawable.Icon
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.ImageView
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.InflationException
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import javax.inject.Inject
+
+/**
+ * Inflates and updates icons associated with notifications
+ *
+ * Notifications are represented by icons in a few different places -- in the status bar, in the
+ * notification shelf, in AOD, etc. This class is in charge of inflating the views that hold these
+ * icons and keeping the icon assets themselves up to date as notifications change.
+ *
+ * TODO: Much of this code was copied whole-sale in order to get it out of NotificationEntry.
+ * Long-term, it should probably live somewhere in the content inflation pipeline.
+ */
+class IconManager @Inject constructor(
+ private val notifCollection: CommonNotifCollection,
+ private val launcherApps: LauncherApps,
+ private val iconBuilder: IconBuilder
+) {
+ fun attach() {
+ notifCollection.addCollectionListener(entryListener)
+ }
+
+ private val entryListener = object : NotifCollectionListener {
+ override fun onEntryInit(entry: NotificationEntry) {
+ entry.addOnSensitivityChangedListener(sensitivityListener)
+ }
+
+ override fun onEntryCleanUp(entry: NotificationEntry) {
+ entry.removeOnSensitivityChangedListener(sensitivityListener)
+ }
+
+ override fun onRankingApplied() {
+ // When the sensitivity changes OR when the isImportantConversation status changes,
+ // we need to update the icons
+ for (entry in notifCollection.allNotifs) {
+ val isImportant = isImportantConversation(entry)
+ if (entry.icons.areIconsAvailable &&
+ isImportant != entry.icons.isImportantConversation) {
+ updateIconsSafe(entry)
+ }
+ entry.icons.isImportantConversation = isImportant
+ }
+ }
+ }
+
+ private val sensitivityListener = NotificationEntry.OnSensitivityChangedListener {
+ entry -> updateIconsSafe(entry)
+ }
+
+ /**
+ * Inflate icon views for each icon variant and assign appropriate icons to them. Stores the
+ * result in [NotificationEntry.getIcons].
+ *
+ * @throws InflationException Exception if required icons are not valid or specified
+ */
+ @Throws(InflationException::class)
+ fun createIcons(entry: NotificationEntry) {
+ // Construct the status bar icon view.
+ val sbIcon = iconBuilder.createIconView(entry)
+ sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+
+ // Construct the shelf icon view.
+ val shelfIcon = iconBuilder.createIconView(entry)
+ shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+
+ shelfIcon.visibility = View.INVISIBLE
+ // TODO: This doesn't belong here
+ shelfIcon.setOnVisibilityChangedListener { newVisibility: Int ->
+ if (entry.row != null) {
+ entry.row.setIconsVisible(newVisibility != View.VISIBLE)
+ }
+ }
+
+ // Construct the aod icon view.
+ val aodIcon = iconBuilder.createIconView(entry)
+ aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+ aodIcon.setIncreasedSize(true)
+
+ // Construct the centered icon view.
+ val centeredIcon = if (entry.sbn.notification.isMediaNotification) {
+ iconBuilder.createIconView(entry).apply {
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
+ } else {
+ null
+ }
+
+ // Set the icon views' icons
+ val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+
+ try {
+ setIcon(entry, normalIconDescriptor, sbIcon)
+ setIcon(entry, sensitiveIconDescriptor, shelfIcon)
+ setIcon(entry, sensitiveIconDescriptor, aodIcon)
+ if (centeredIcon != null) {
+ setIcon(entry, normalIconDescriptor, centeredIcon)
+ }
+ entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, centeredIcon, entry.icons)
+ } catch (e: InflationException) {
+ entry.icons = IconPack.buildEmptyPack(entry.icons)
+ throw e
+ }
+ }
+
+ /**
+ * Update the notification icons.
+ *
+ * @param entry the notification to read the icon from.
+ * @throws InflationException Exception if required icons are not valid or specified
+ */
+ @Throws(InflationException::class)
+ fun updateIcons(entry: NotificationEntry) {
+ if (!entry.icons.areIconsAvailable) {
+ return
+ }
+ entry.icons.smallIconDescriptor = null
+ entry.icons.peopleAvatarDescriptor = null
+
+ val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+
+ entry.icons.statusBarIcon?.let {
+ it.notification = entry.sbn
+ setIcon(entry, normalIconDescriptor, it)
+ }
+
+ entry.icons.shelfIcon?.let {
+ it.notification = entry.sbn
+ setIcon(entry, normalIconDescriptor, it)
+ }
+
+ entry.icons.aodIcon?.let {
+ it.notification = entry.sbn
+ setIcon(entry, sensitiveIconDescriptor, it)
+ }
+
+ entry.icons.centeredIcon?.let {
+ it.notification = entry.sbn
+ setIcon(entry, sensitiveIconDescriptor, it)
+ }
+ }
+
+ /**
+ * Updates tags on the icon views to match the posting app's target SDK level
+ *
+ * Note that this method MUST be called after both [createIcons] and [updateIcons].
+ */
+ fun updateIconTags(entry: NotificationEntry, targetSdk: Int) {
+ setTagOnIconViews(
+ entry.icons,
+ R.id.icon_is_pre_L,
+ targetSdk < Build.VERSION_CODES.LOLLIPOP)
+ }
+
+ private fun updateIconsSafe(entry: NotificationEntry) {
+ try {
+ updateIcons(entry)
+ } catch (e: InflationException) {
+ // TODO This should mark the entire row as involved in an inflation error
+ Log.e(TAG, "Unable to update icon", e)
+ }
+ }
+
+ @Throws(InflationException::class)
+ private fun getIconDescriptors(
+ entry: NotificationEntry
+ ): Pair<StatusBarIcon, StatusBarIcon> {
+ val iconDescriptor = getIconDescriptor(entry, false /* redact */)
+ val sensitiveDescriptor = if (entry.isSensitive) {
+ getIconDescriptor(entry, true /* redact */)
+ } else {
+ iconDescriptor
+ }
+ return Pair(iconDescriptor, sensitiveDescriptor)
+ }
+
+ @Throws(InflationException::class)
+ private fun getIconDescriptor(
+ entry: NotificationEntry,
+ redact: Boolean
+ ): StatusBarIcon {
+ val n = entry.sbn.notification
+ val showPeopleAvatar = isImportantConversation(entry) && !redact
+
+ val peopleAvatarDescriptor = entry.icons.peopleAvatarDescriptor
+ val smallIconDescriptor = entry.icons.smallIconDescriptor
+
+ // If cached, return corresponding cached values
+ if (showPeopleAvatar && peopleAvatarDescriptor != null) {
+ return peopleAvatarDescriptor
+ } else if (!showPeopleAvatar && smallIconDescriptor != null) {
+ return smallIconDescriptor
+ }
+
+ val icon =
+ (if (showPeopleAvatar) {
+ createPeopleAvatar(entry)
+ } else {
+ n.smallIcon
+ }) ?: throw InflationException(
+ "No icon in notification from " + entry.sbn.packageName)
+
+ val ic = StatusBarIcon(
+ entry.sbn.user,
+ entry.sbn.packageName,
+ icon,
+ n.iconLevel,
+ n.number,
+ iconBuilder.getIconContentDescription(n))
+
+ // Cache if important conversation.
+ if (isImportantConversation(entry)) {
+ if (showPeopleAvatar) {
+ entry.icons.peopleAvatarDescriptor = ic
+ } else {
+ entry.icons.smallIconDescriptor = ic
+ }
+ }
+
+ return ic
+ }
+
+ @Throws(InflationException::class)
+ private fun setIcon(
+ entry: NotificationEntry,
+ iconDescriptor: StatusBarIcon,
+ iconView: StatusBarIconView
+ ) {
+ iconView.setTintIcons(shouldTintIconView(entry, iconView))
+ if (!iconView.set(iconDescriptor)) {
+ throw InflationException("Couldn't create icon $iconDescriptor")
+ }
+ }
+
+ @Throws(InflationException::class)
+ private fun createPeopleAvatar(entry: NotificationEntry): Icon? {
+ // Attempt to extract form shortcut.
+ val conversationId = entry.ranking.channel.conversationId
+ val query = LauncherApps.ShortcutQuery()
+ .setPackage(entry.sbn.packageName)
+ .setQueryFlags(
+ LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
+ or LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)
+ .setShortcutIds(listOf(conversationId))
+ val shortcuts = launcherApps.getShortcuts(query, entry.sbn.user)
+ var ic: Icon? = null
+ if (shortcuts != null && shortcuts.isNotEmpty()) {
+ ic = shortcuts[0].icon
+ }
+
+ // Fall back to notification large icon if available
+ if (ic == null) {
+ ic = entry.sbn.notification.getLargeIcon()
+ }
+
+ // Fall back to extract from message
+ if (ic == null) {
+ val extras: Bundle = entry.sbn.notification.extras
+ val messages = Notification.MessagingStyle.Message.getMessagesFromBundleArray(
+ extras.getParcelableArray(Notification.EXTRA_MESSAGES))
+ val user = extras.getParcelable<Person>(Notification.EXTRA_MESSAGING_PERSON)
+ for (i in messages.indices.reversed()) {
+ val message = messages[i]
+ val sender = message.senderPerson
+ if (sender != null && sender !== user) {
+ ic = message.senderPerson!!.icon
+ break
+ }
+ }
+ }
+
+ // Revert to small icon if still not available
+ if (ic == null) {
+ ic = entry.sbn.notification.smallIcon
+ }
+ if (ic == null) {
+ throw InflationException("No icon in notification from " + entry.sbn.packageName)
+ }
+ return ic
+ }
+
+ /**
+ * Determines if this icon should be tinted based on the sensitivity of the icon, its context
+ * and the user's indicated sensitivity preference.
+ *
+ * @param iconView The icon that should/should not be tinted.
+ */
+ private fun shouldTintIconView(entry: NotificationEntry, iconView: StatusBarIconView): Boolean {
+ val usedInSensitiveContext =
+ iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
+ return !isImportantConversation(entry) || usedInSensitiveContext && entry.isSensitive
+ }
+
+ private fun isImportantConversation(entry: NotificationEntry): Boolean {
+ return entry.ranking.channel != null && entry.ranking.channel.isImportantConversation
+ }
+
+ private fun setTagOnIconViews(icons: IconPack, key: Int, tag: Any) {
+ icons.statusBarIcon?.setTag(key, tag)
+ icons.shelfIcon?.setTag(key, tag)
+ icons.aodIcon?.setTag(key, tag)
+ icons.centeredIcon?.setTag(key, tag)
+ }
+}
+
+private const val TAG = "IconManager"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
new file mode 100644
index 0000000..054e381
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
@@ -0,0 +1,134 @@
+/*
+ * 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.icon;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.StatusBarIconView;
+
+/**
+ * Data class for storing icons associated with a notification
+ */
+public final class IconPack {
+
+ private final boolean mAreIconsAvailable;
+ @Nullable private final StatusBarIconView mStatusBarIcon;
+ @Nullable private final StatusBarIconView mShelfIcon;
+ @Nullable private final StatusBarIconView mAodIcon;
+ @Nullable private final StatusBarIconView mCenteredIcon;
+
+ @Nullable private StatusBarIcon mSmallIconDescriptor;
+ @Nullable private StatusBarIcon mPeopleAvatarDescriptor;
+
+ private boolean mIsImportantConversation;
+
+ /**
+ * Builds an empty instance of IconPack that doesn't have any icons (because either they
+ * haven't been inflated yet or there was an error while inflating them).
+ */
+ public static IconPack buildEmptyPack(@Nullable IconPack fromSource) {
+ return new IconPack(false, null, null, null, null, fromSource);
+ }
+
+ /**
+ * Builds an instance of an IconPack that contains successfully-inflated icons
+ */
+ public static IconPack buildPack(
+ @NonNull StatusBarIconView statusBarIcon,
+ @NonNull StatusBarIconView shelfIcon,
+ @NonNull StatusBarIconView aodIcon,
+ @Nullable StatusBarIconView centeredIcon,
+ @Nullable IconPack source) {
+ return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, centeredIcon, source);
+ }
+
+ private IconPack(
+ boolean areIconsAvailable,
+ @Nullable StatusBarIconView statusBarIcon,
+ @Nullable StatusBarIconView shelfIcon,
+ @Nullable StatusBarIconView aodIcon,
+ @Nullable StatusBarIconView centeredIcon,
+ @Nullable IconPack source) {
+ mAreIconsAvailable = areIconsAvailable;
+ mStatusBarIcon = statusBarIcon;
+ mShelfIcon = shelfIcon;
+ mCenteredIcon = centeredIcon;
+ mAodIcon = aodIcon;
+ if (source != null) {
+ mIsImportantConversation = source.mIsImportantConversation;
+ }
+ }
+
+ /** The version of the notification icon that appears in the status bar. */
+ @Nullable
+ public StatusBarIconView getStatusBarIcon() {
+ return mStatusBarIcon;
+ }
+
+ /**
+ * The version of the icon that appears in the "shelf" at the bottom of the notification shade.
+ * In general, this icon also appears somewhere on the notification and is "sucked" into the
+ * shelf as the scrolls beyond it.
+ */
+ @Nullable
+ public StatusBarIconView getShelfIcon() {
+ return mShelfIcon;
+ }
+
+ @Nullable
+ public StatusBarIconView getCenteredIcon() {
+ return mCenteredIcon;
+ }
+
+ /** The version of the icon that's shown when pulsing (in AOD). */
+ @Nullable
+ public StatusBarIconView getAodIcon() {
+ return mAodIcon;
+ }
+
+ @Nullable
+ StatusBarIcon getSmallIconDescriptor() {
+ return mSmallIconDescriptor;
+ }
+
+ void setSmallIconDescriptor(@Nullable StatusBarIcon smallIconDescriptor) {
+ mSmallIconDescriptor = smallIconDescriptor;
+ }
+
+ @Nullable
+ StatusBarIcon getPeopleAvatarDescriptor() {
+ return mPeopleAvatarDescriptor;
+ }
+
+ void setPeopleAvatarDescriptor(@Nullable StatusBarIcon peopleAvatarDescriptor) {
+ mPeopleAvatarDescriptor = peopleAvatarDescriptor;
+ }
+
+ boolean isImportantConversation() {
+ return mIsImportantConversation;
+ }
+
+ void setImportantConversation(boolean importantConversation) {
+ mIsImportantConversation = importantConversation;
+ }
+
+ public boolean getAreIconsAvailable() {
+ return mAreIconsAvailable;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 8a6d5c7..7a7178c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -94,7 +94,10 @@
notifBindPipelineInitializer.initialize()
if (featureFlags.isNewNotifPipelineEnabled) {
- newNotifPipeline.get().initialize(notificationListener, notificationRowBinder)
+ newNotifPipeline.get().initialize(
+ notificationListener,
+ notificationRowBinder,
+ listContainer)
}
if (featureFlags.isNewNotifPipelineRenderingEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
index 269a7a5..88888d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification
+package com.android.systemui.statusbar.notification.interruption
import android.content.Context
import android.media.MediaMetadata
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
index df21f0b..b572502 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.interruption;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -27,6 +27,9 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -39,7 +42,7 @@
private final NotificationRemoteInputManager mRemoteInputManager;
private final VisualStabilityManager mVisualStabilityManager;
private final StatusBarStateController mStatusBarStateController;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationListener mNotificationListener;
private HeadsUpManager mHeadsUpManager;
@@ -52,13 +55,13 @@
NotificationRemoteInputManager remoteInputManager,
VisualStabilityManager visualStabilityManager,
StatusBarStateController statusBarStateController,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptionStateProvider,
NotificationListener notificationListener,
HeadsUpManager headsUpManager) {
mRemoteInputManager = remoteInputManager;
mVisualStabilityManager = visualStabilityManager;
mStatusBarStateController = statusBarStateController;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
mNotificationListener = notificationListener;
mHeadsUpManager = headsUpManager;
@@ -94,7 +97,7 @@
if (entry.getRow().getPrivateLayout().getHeadsUpChild() != null) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
// If it does and we no longer need to heads up, we should free the view.
- if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+ if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
mHeadsUpManager.showNotification(entry);
if (!mStatusBarStateController.isDozing()) {
// Mark as seen immediately
@@ -109,7 +112,7 @@
private void updateAlertState(NotificationEntry entry) {
boolean alertAgain = alertAgain(entry, entry.getSbn().getNotification());
// includes check for whether this notification should be filtered:
- boolean shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+ boolean shouldAlert = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.getKey());
if (wasAlerting) {
if (shouldAlert) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
new file mode 100644
index 0000000..3292a8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.interruption;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Provides bubble-up and heads-up state for notification entries.
+ *
+ * When a notification is heads-up when dozing, this is also called "pulsing."
+ */
+public interface NotificationInterruptStateProvider {
+ /**
+ * If the device is awake (not dozing):
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * If the device is dozing:
+ * Whether the notification should show the ambient view of the notification ("pulse").
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ boolean shouldHeadsUp(NotificationEntry entry);
+
+ /**
+ * Whether the notification should appear as a bubble with a fly-out on top of the screen.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should bubble up, false otherwise
+ */
+ boolean shouldBubbleUp(NotificationEntry entry);
+
+ /**
+ * Whether to launch the entry's full screen intent when the entry is added.
+ *
+ * @param entry the entry that was added
+ * @return {@code true} if we should launch the full screen intent
+ */
+ boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry);
+
+ /**
+ * Add a component that can suppress visual interruptions.
+ */
+ void addSuppressor(NotificationInterruptSuppressor suppressor);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index bbf2dde..46d5044 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -14,33 +14,35 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.interruption;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import android.app.NotificationManager;
-import android.content.Context;
+import android.content.ContentResolver;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -48,120 +50,84 @@
* Provides heads-up and pulsing state for notification entries.
*/
@Singleton
-public class NotificationInterruptionStateProvider {
-
+public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider {
private static final String TAG = "InterruptionStateProvider";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true; //false;
private static final boolean DEBUG_HEADS_UP = true;
private static final boolean ENABLE_HEADS_UP = true;
private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+ private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>();
private final StatusBarStateController mStatusBarStateController;
private final NotificationFilter mNotificationFilter;
- private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
-
- private final Context mContext;
+ private final ContentResolver mContentResolver;
private final PowerManager mPowerManager;
private final IDreamManager mDreamManager;
+ private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final BatteryController mBatteryController;
-
- private NotificationPresenter mPresenter;
+ private final ContentObserver mHeadsUpObserver;
private HeadsUpManager mHeadsUpManager;
- private HeadsUpSuppressor mHeadsUpSuppressor;
- private ContentObserver mHeadsUpObserver;
@VisibleForTesting
protected boolean mUseHeadsUp = false;
- private boolean mDisableNotificationAlerts;
@Inject
- public NotificationInterruptionStateProvider(Context context, NotificationFilter filter,
- StatusBarStateController stateController, BatteryController batteryController) {
- this(context,
- (PowerManager) context.getSystemService(Context.POWER_SERVICE),
- IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE)),
- new AmbientDisplayConfiguration(context),
- filter,
- batteryController,
- stateController);
- }
-
- @VisibleForTesting
- protected NotificationInterruptionStateProvider(
- Context context,
+ public NotificationInterruptStateProviderImpl(
+ ContentResolver contentResolver,
PowerManager powerManager,
IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter notificationFilter,
BatteryController batteryController,
- StatusBarStateController statusBarStateController) {
- mContext = context;
+ StatusBarStateController statusBarStateController,
+ HeadsUpManager headsUpManager,
+ @Main Handler mainHandler) {
+ mContentResolver = contentResolver;
mPowerManager = powerManager;
mDreamManager = dreamManager;
mBatteryController = batteryController;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mNotificationFilter = notificationFilter;
mStatusBarStateController = statusBarStateController;
- }
-
- /** Sets up late-binding dependencies for this component. */
- public void setUpWithPresenter(
- NotificationPresenter notificationPresenter,
- HeadsUpManager headsUpManager,
- HeadsUpSuppressor headsUpSuppressor) {
- setUpWithPresenter(notificationPresenter, headsUpManager, headsUpSuppressor,
- new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
- @Override
- public void onChange(boolean selfChange) {
- boolean wasUsing = mUseHeadsUp;
- mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
- && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
- Settings.Global.HEADS_UP_OFF);
- Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
- if (wasUsing != mUseHeadsUp) {
- if (!mUseHeadsUp) {
- Log.d(TAG,
- "dismissing any existing heads up notification on disable"
- + " event");
- mHeadsUpManager.releaseAllImmediately();
- }
- }
- }
- });
- }
-
- /** Sets up late-binding dependencies for this component. */
- public void setUpWithPresenter(
- NotificationPresenter notificationPresenter,
- HeadsUpManager headsUpManager,
- HeadsUpSuppressor headsUpSuppressor,
- ContentObserver observer) {
- mPresenter = notificationPresenter;
mHeadsUpManager = headsUpManager;
- mHeadsUpSuppressor = headsUpSuppressor;
- mHeadsUpObserver = observer;
+ mHeadsUpObserver = new ContentObserver(mainHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ boolean wasUsing = mUseHeadsUp;
+ mUseHeadsUp = ENABLE_HEADS_UP
+ && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+ mContentResolver,
+ Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ Settings.Global.HEADS_UP_OFF);
+ Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+ if (wasUsing != mUseHeadsUp) {
+ if (!mUseHeadsUp) {
+ Log.d(TAG, "dismissing any existing heads up notification on "
+ + "disable event");
+ mHeadsUpManager.releaseAllImmediately();
+ }
+ }
+ }
+ };
if (ENABLE_HEADS_UP) {
- mContext.getContentResolver().registerContentObserver(
+ mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
true,
mHeadsUpObserver);
- mContext.getContentResolver().registerContentObserver(
+ mContentResolver.registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
mHeadsUpObserver);
}
mHeadsUpObserver.onChange(true); // set up
}
- /**
- * Whether the notification should appear as a bubble with a fly-out on top of the screen.
- *
- * @param entry the entry to check
- * @return true if the entry should bubble up, false otherwise
- */
+ @Override
+ public void addSuppressor(NotificationInterruptSuppressor suppressor) {
+ mSuppressors.add(suppressor);
+ }
+
+ @Override
public boolean shouldBubbleUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
@@ -201,12 +167,8 @@
return true;
}
- /**
- * Whether the notification should peek in from the top and alert the user.
- *
- * @param entry the entry to check
- * @return true if the entry should heads up, false otherwise
- */
+
+ @Override
public boolean shouldHeadsUp(NotificationEntry entry) {
if (mStatusBarStateController.isDozing()) {
return shouldHeadsUpWhenDozing(entry);
@@ -215,6 +177,17 @@
}
}
+ /**
+ * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or
+ * incoming calls.
+ */
+ @Override
+ public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
+ return entry.getSbn().getNotification().fullScreenIntent != null
+ && (!shouldHeadsUp(entry)
+ || mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
+ }
+
private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
StatusBarNotification sbn = entry.getSbn();
@@ -271,13 +244,15 @@
return false;
}
- if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
- if (DEBUG_HEADS_UP) {
- Log.d(TAG, "No heads up: aborted by suppressor: " + sbn.getKey());
+ for (int i = 0; i < mSuppressors.size(); i++) {
+ if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
+ if (DEBUG_HEADS_UP) {
+ Log.d(TAG, "No heads up: aborted by suppressor: "
+ + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+ }
+ return false;
}
- return false;
}
-
return true;
}
@@ -325,7 +300,7 @@
}
return false;
}
- return true;
+ return true;
}
/**
@@ -334,8 +309,7 @@
* @param entry the entry to check
* @return true if these checks pass, false if the notification should not alert
*/
- @VisibleForTesting
- public boolean canAlertCommon(NotificationEntry entry) {
+ private boolean canAlertCommon(NotificationEntry entry) {
StatusBarNotification sbn = entry.getSbn();
if (mNotificationFilter.shouldFilterOut(entry)) {
@@ -352,6 +326,16 @@
}
return false;
}
+
+ for (int i = 0; i < mSuppressors.size(); i++) {
+ if (mSuppressors.get(i).suppressInterruptions(entry)) {
+ if (DEBUG_HEADS_UP) {
+ Log.d(TAG, "No alerting: aborted by suppressor: "
+ + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+ }
+ return false;
+ }
+ }
return true;
}
@@ -361,15 +345,17 @@
* @param entry the entry to check
* @return true if these checks pass, false if the notification should not alert
*/
- @VisibleForTesting
- public boolean canAlertAwakeCommon(NotificationEntry entry) {
+ private boolean canAlertAwakeCommon(NotificationEntry entry) {
StatusBarNotification sbn = entry.getSbn();
- if (mPresenter.isDeviceInVrMode()) {
- if (DEBUG_HEADS_UP) {
- Log.d(TAG, "No alerting: no huns or vr mode");
+ for (int i = 0; i < mSuppressors.size(); i++) {
+ if (mSuppressors.get(i).suppressAwakeInterruptions(entry)) {
+ if (DEBUG_HEADS_UP) {
+ Log.d(TAG, "No alerting: aborted by suppressor: "
+ + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+ }
+ return false;
}
- return false;
}
if (isSnoozedPackage(sbn)) {
@@ -392,54 +378,4 @@
private boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
-
- /** Sets whether to disable all alerts. */
- public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
- mDisableNotificationAlerts = disableNotificationAlerts;
- mHeadsUpObserver.onChange(true);
- }
-
- /** Whether all alerts are disabled. */
- @VisibleForTesting
- public boolean areNotificationAlertsDisabled() {
- return mDisableNotificationAlerts;
- }
-
- /** Whether HUNs should be used. */
- @VisibleForTesting
- public boolean getUseHeadsUp() {
- return mUseHeadsUp;
- }
-
- protected NotificationPresenter getPresenter() {
- return mPresenter;
- }
-
- /**
- * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or
- * incoming calls.
- *
- * @param entry the entry that was added
- * @return {@code true} if we should launch the full screen intent
- */
- public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
- return entry.getSbn().getNotification().fullScreenIntent != null
- && (!shouldHeadsUp(entry)
- || mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
- }
-
- /** A component which can suppress heads-up notifications due to the overall state of the UI. */
- public interface HeadsUpSuppressor {
- /**
- * Returns false if the provided notification is ineligible for heads-up according to this
- * component.
- *
- * @param entry entry of the notification that might be heads upped
- * @param sbn notification that might be heads upped
- * @return false if the notification can not be heads upped
- */
- boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn);
-
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java
new file mode 100644
index 0000000..c19f8bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.interruption;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/** A component which can suppress visual interruptions of notifications such as heads-up and
+ * bubble-up.
+ */
+public interface NotificationInterruptSuppressor {
+ /**
+ * A unique name to identify this suppressor.
+ */
+ default String getName() {
+ return this.getClass().getName();
+ }
+
+ /**
+ * Returns true if the provided notification is, when the device is awake, ineligible for
+ * heads-up according to this component.
+ *
+ * @param entry entry of the notification that might heads-up
+ * @return true if the heads up interruption should be suppressed when the device is awake
+ */
+ default boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+ return false;
+ }
+
+ /**
+ * Returns true if the provided notification is, when the device is awake, ineligible for
+ * heads-up or bubble-up according to this component.
+ *
+ * @param entry entry of the notification that might heads-up or bubble-up
+ * @return true if interruptions should be suppressed when the device is awake
+ */
+ default boolean suppressAwakeInterruptions(NotificationEntry entry) {
+ return false;
+ }
+
+ /**
+ * Returns true if the provided notification is, regardless of awake/dozing state,
+ * ineligible for heads-up or bubble-up according to this component.
+ *
+ * @param entry entry of the notification that might heads-up or bubble-up
+ * @return true if interruptions should be suppressed
+ */
+ default boolean suppressInterruptions(NotificationEntry entry) {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 597bdb9..be3873a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -83,7 +83,7 @@
private val Ranking.personTypeInfo
get() = when {
- channel.isImportantConversation -> TYPE_IMPORTANT_PERSON
+ channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON
isConversation -> TYPE_PERSON
else -> TYPE_NON_PERSON
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index ea1bdd6..b9dd974 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -271,6 +271,10 @@
}
}
+ public boolean isActive() {
+ return mActivated;
+ }
+
private void startActivateAnimation(final boolean reverse) {
if (!isAttachedToWindow()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 2643ec9..2f0e433 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -111,7 +111,7 @@
if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
&& mView.disallowSingleClick(ev)
&& !mAccessibilityManager.isTouchExplorationEnabled()) {
- if (!mView.isActivated()) {
+ if (!mView.isActive()) {
return true;
} else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
mBlockNextTouch = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
index 373457d..dbfa27f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
@@ -37,7 +37,7 @@
}
(findViewById(R.id.icon) as StatusBarIconView).apply {
- set(entry?.icon?.statusBarIcon)
+ set(entry?.icons?.statusBarIcon?.statusBarIcon)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9a4e789..5f2b256 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -31,7 +31,6 @@
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -95,6 +94,7 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationListItem;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -116,7 +116,8 @@
* the group summary (which contains 1 or more child notifications).
*/
public class ExpandableNotificationRow extends ActivatableNotificationView
- implements PluginListener<NotificationMenuRowPlugin>, SwipeableView {
+ implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
+ NotificationListItem {
private static final boolean DEBUG = false;
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
@@ -151,7 +152,6 @@
private int mNotificationMinHeight;
private int mNotificationMinHeightLarge;
private int mNotificationMinHeightMedia;
- private int mNotificationMinHeightMessaging;
private int mNotificationMaxHeight;
private int mIncreasedPaddingBetweenElements;
private int mNotificationLaunchHeight;
@@ -581,7 +581,7 @@
@VisibleForTesting
void updateShelfIconColor() {
- StatusBarIconView expandedIcon = mEntry.expandedIcon;
+ StatusBarIconView expandedIcon = mEntry.getIcons().getShelfIcon();
boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
ContrastColorUtil.getInstance(mContext));
@@ -640,16 +640,10 @@
&& expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
- Class<? extends Notification.Style> style =
- mEntry.getSbn().getNotification().getNotificationStyle();
- boolean isMessagingLayout = Notification.MessagingStyle.class.equals(style);
-
if (customView && beforeP && !mIsSummaryWithChildren) {
minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP;
} else if (isMediaLayout && showCompactMediaSeekbar) {
minHeight = mNotificationMinHeightMedia;
- } else if (isMessagingLayout) {
- minHeight = mNotificationMinHeightMessaging;
} else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
minHeight = mNotificationMinHeightLarge;
} else {
@@ -674,6 +668,7 @@
layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight);
}
+ @NonNull
public NotificationEntry getEntry() {
return mEntry;
}
@@ -775,6 +770,17 @@
row.setIsChildInGroup(true, this);
}
+ /**
+ * Same as {@link #addChildNotification(ExpandableNotificationRow, int)}, but takes a
+ * {@link NotificationListItem} instead
+ *
+ * @param childItem item
+ * @param childIndex index
+ */
+ public void addChildNotification(NotificationListItem childItem, int childIndex) {
+ addChildNotification((ExpandableNotificationRow) childItem.getView(), childIndex);
+ }
+
public void removeChildNotification(ExpandableNotificationRow row) {
if (mChildrenContainer != null) {
mChildrenContainer.removeNotification(row);
@@ -785,6 +791,11 @@
}
@Override
+ public void removeChildNotification(NotificationListItem child) {
+ removeChildNotification((ExpandableNotificationRow) child.getView());
+ }
+
+ @Override
public boolean isChildInGroup() {
return mNotificationParent != null;
}
@@ -887,7 +898,7 @@
* @param callback the callback to invoked in case it is not allowed
* @return whether the list order has changed
*/
- public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
+ public boolean applyChildOrder(List<? extends NotificationListItem> childOrder,
VisualStabilityManager visualStabilityManager,
VisualStabilityManager.Callback callback) {
return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
@@ -1057,19 +1068,6 @@
return getShowingLayout().getVisibleNotificationHeader();
}
-
- /**
- * @return the contracted notification header. This can be different from
- * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
- * returns the contracted version.
- */
- public NotificationHeaderView getContractedNotificationHeader() {
- if (mIsSummaryWithChildren) {
- return mChildrenContainer.getHeaderView();
- }
- return mPrivateLayout.getContractedNotificationHeader();
- }
-
public void setLongPressListener(LongPressListener longPressListener) {
mLongPressListener = longPressListener;
}
@@ -1295,6 +1293,11 @@
onChildrenCountChanged();
}
+ @Override
+ public View getView() {
+ return this;
+ }
+
public void setForceUnlocked(boolean forceUnlocked) {
mForceUnlocked = forceUnlocked;
if (mIsSummaryWithChildren) {
@@ -1311,7 +1314,7 @@
setLongPressListener(null);
mGroupParentWhenDismissed = mNotificationParent;
mChildAfterViewWhenDismissed = null;
- mEntry.icon.setDismissed();
+ mEntry.getIcons().getStatusBarIcon().setDismissed();
if (isChildInGroup()) {
List<ExpandableNotificationRow> notificationChildren =
mNotificationParent.getNotificationChildren();
@@ -1654,8 +1657,6 @@
R.dimen.notification_min_height_increased);
mNotificationMinHeightMedia = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height_media);
- mNotificationMinHeightMessaging = NotificationUtils.getFontScaledHeight(mContext,
- R.dimen.notification_min_height_messaging);
mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
@@ -1855,7 +1856,7 @@
mTranslateableViews.get(i).setTranslationX(0);
}
invalidateOutline();
- getEntry().expandedIcon.setScrollX(0);
+ getEntry().getIcons().getShelfIcon().setScrollX(0);
}
if (mMenuRow != null) {
@@ -1935,7 +1936,7 @@
// In order to keep the shelf in sync with this swiping, we're simply translating
// it's icon by the same amount. The translation is already being used for the normal
// positioning, so we can use the scrollX instead.
- getEntry().expandedIcon.setScrollX((int) -translationX);
+ getEntry().getIcons().getShelfIcon().setScrollX((int) -translationX);
}
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
@@ -2134,7 +2135,7 @@
@Override
public StatusBarIconView getShelfIcon() {
- return getEntry().expandedIcon;
+ return getEntry().getIcons().getShelfIcon();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 6dd4ff9..91cf285 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -25,6 +25,8 @@
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.service.notification.StatusBarNotification;
@@ -716,6 +718,10 @@
sbn.getNotification());
Context packageContext = sbn.getPackageContext(mContext);
+ if (recoveredBuilder.usesTemplate()) {
+ // For all of our templates, we want it to be RTL
+ packageContext = new RtlEnabledContext(packageContext);
+ }
Notification notification = sbn.getNotification();
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
@@ -782,6 +788,19 @@
// try to purge unnecessary cached entries.
mRow.getImageResolver().purgeCache();
}
+
+ private class RtlEnabledContext extends ContextWrapper {
+ private RtlEnabledContext(Context packageContext) {
+ super(packageContext);
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo() {
+ ApplicationInfo applicationInfo = super.getApplicationInfo();
+ applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
+ return applicationInfo;
+ }
+ }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 27fd1b2..8b8a901 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1492,13 +1492,6 @@
}
}
- public NotificationHeaderView getContractedNotificationHeader() {
- if (mContractedChild != null) {
- return mContractedWrapper.getNotificationHeader();
- }
- return null;
- }
-
public NotificationHeaderView getVisibleNotificationHeader() {
NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
return wrapper == null ? null : wrapper.getNotificationHeader();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
new file mode 100644
index 0000000..162786c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.row.wrapper
+
+import android.content.Context
+import android.view.View
+
+import com.android.internal.widget.ConversationLayout
+import com.android.internal.widget.MessagingLinearLayout
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/**
+ * Wraps a notification containing a converation template
+ */
+class NotificationConversationTemplateViewWrapper constructor(
+ ctx: Context,
+ view: View,
+ row: ExpandableNotificationRow
+)
+ : NotificationTemplateViewWrapper(ctx, view, row) {
+
+ private val minHeightWithActions: Int
+ private val conversationLayout: ConversationLayout
+ private var conversationIcon: View? = null
+ private var conversationBadge: View? = null
+ private var expandButton: View? = null
+ private lateinit var expandButtonContainer: View
+ private var messagingLinearLayout: MessagingLinearLayout? = null
+
+ init {
+ conversationLayout = view as ConversationLayout
+ minHeightWithActions = NotificationUtils.getFontScaledHeight(ctx,
+ R.dimen.notification_messaging_actions_min_height)
+ }
+
+ private fun resolveViews() {
+ messagingLinearLayout = conversationLayout.messagingLinearLayout
+ conversationIcon = conversationLayout.requireViewById(
+ com.android.internal.R.id.conversation_icon)
+ conversationBadge = conversationLayout.requireViewById(
+ com.android.internal.R.id.conversation_icon_badge)
+ expandButton = conversationLayout.requireViewById(
+ com.android.internal.R.id.expand_button)
+ expandButtonContainer = conversationLayout.requireViewById(
+ com.android.internal.R.id.expand_button_container)
+ }
+
+ override fun onContentUpdated(row: ExpandableNotificationRow) {
+ // Reinspect the notification. Before the super call, because the super call also updates
+ // the transformation types and we need to have our values set by then.
+ resolveViews()
+ super.onContentUpdated(row)
+ }
+
+ override fun updateTransformedTypes() {
+ // This also clears the existing types
+ super.updateTransformedTypes()
+ messagingLinearLayout?.let {
+ mTransformationHelper.addTransformedView(it.id, it)
+ }
+ conversationIcon?.let {
+ mTransformationHelper.addViewTransformingToSimilar(it.id, it)
+ }
+ conversationBadge?.let {
+ mTransformationHelper.addViewTransformingToSimilar(it.id, it)
+ }
+ expandButton?.let {
+ mTransformationHelper.addViewTransformingToSimilar(it.id, it)
+ }
+ }
+
+ override fun setRemoteInputVisible(visible: Boolean) {
+ conversationLayout.showHistoricMessages(visible)
+ }
+
+ override fun updateExpandability(expandable: Boolean, onClickListener: View.OnClickListener?) {
+ conversationLayout.updateExpandability(expandable, onClickListener)
+ }
+
+ override fun disallowSingleClick(x: Float, y: Float): Boolean {
+ if (expandButtonContainer.visibility == View.VISIBLE
+ && isOnView(expandButtonContainer, x, y)) {
+ return true
+ }
+ return super.disallowSingleClick(x, y)
+ }
+
+ override fun getMinLayoutHeight(): Int {
+ if (mActionsContainer != null && mActionsContainer.visibility != View.GONE) {
+ return minHeightWithActions
+ } else {
+ return super.getMinLayoutHeight()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 5e52c0a..1d06198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -53,12 +53,13 @@
protected final ViewTransformationHelper mTransformationHelper;
protected int mColor;
- private ImageView mIcon;
+ private ImageView mIcon;
private NotificationExpandButton mExpandButton;
protected NotificationHeaderView mNotificationHeader;
private TextView mHeaderText;
private ImageView mWorkProfileImage;
+
private boolean mIsLowPriority;
private boolean mTransformLowPriorityTitle;
private boolean mShowExpandButtonAtEnd;
@@ -105,12 +106,16 @@
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
- mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
- mColor = mNotificationHeader.getOriginalIconColor();
+ if (mNotificationHeader != null) {
+ mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
+ mColor = mNotificationHeader.getOriginalIconColor();
+ }
}
private void addAppOpsOnClickListener(ExpandableNotificationRow row) {
- mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
+ if (mNotificationHeader != null) {
+ mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener());
+ }
}
@Override
@@ -127,9 +132,11 @@
updateCropToPaddingForImageViews();
Notification notification = row.getEntry().getSbn().getNotification();
mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
- // The work profile image is always the same lets just set the icon tag for it not to
- // animate
- mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
+ if (mWorkProfileImage != null) {
+ // The work profile image is always the same lets just set the icon tag for it not to
+ // animate
+ mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
+ }
// We need to reset all views that are no longer transforming in case a view was previously
// transformed, but now we decided to transform its container instead.
@@ -174,8 +181,9 @@
protected void updateTransformedTypes() {
mTransformationHelper.reset();
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
- if (mIsLowPriority) {
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON,
+ mIcon);
+ if (mIsLowPriority && mHeaderText != null) {
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
mHeaderText);
}
@@ -184,7 +192,9 @@
@Override
public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
- mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
+ if (mNotificationHeader != null) {
+ mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 0a1a2fe..2d99ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -58,7 +58,6 @@
private TextView mText;
protected View mActionsContainer;
private ImageView mReplyAction;
- private Rect mTmpRect = new Rect();
private int mContentHeight;
private int mMinHeightHint;
@@ -271,18 +270,6 @@
return super.disallowSingleClick(x, y);
}
- private boolean isOnView(View view, float x, float y) {
- View searchView = (View) view.getParent();
- while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
- searchView.getHitRect(mTmpRect);
- x -= mTmpRect.left;
- y -= mTmpRect.top;
- searchView = (View) searchView.getParent();
- }
- view.getHitRect(mTmpRect);
- return mTmpRect.contains((int) x,(int) y);
- }
-
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
@@ -353,8 +340,12 @@
@Override
public void setHeaderVisibleAmount(float headerVisibleAmount) {
super.setHeaderVisibleAmount(headerVisibleAmount);
- mNotificationHeader.setAlpha(headerVisibleAmount);
- mHeaderTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation;
+ float headerTranslation = 0f;
+ if (mNotificationHeader != null) {
+ mNotificationHeader.setAlpha(headerVisibleAmount);
+ headerTranslation = (1.0f - headerVisibleAmount) * mFullHeaderTranslation;
+ }
+ mHeaderTranslation = headerTranslation;
mView.setTranslationY(mHeaderTranslation);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index c2eff8a..46d7d93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -24,6 +24,7 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -35,6 +36,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.ConversationLayout;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.TransformState;
@@ -48,6 +50,7 @@
protected final View mView;
protected final ExpandableNotificationRow mRow;
+ private final Rect mTmpRect = new Rect();
protected int mBackgroundColor = 0;
@@ -61,6 +64,9 @@
return new NotificationMediaTemplateViewWrapper(ctx, v, row);
} else if ("messaging".equals(v.getTag())) {
return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
+ } else if ("conversation".equals(v.getTag())) {
+ return new NotificationConversationTemplateViewWrapper(ctx, (ConversationLayout) v,
+ row);
}
Class<? extends Notification.Style> style =
row.getEntry().getSbn().getNotification().getNotificationStyle();
@@ -301,6 +307,26 @@
return false;
}
+ /**
+ * Is a given x and y coordinate on a view.
+ *
+ * @param view the view to be checked
+ * @param x the x coordinate, relative to the ExpandableNotificationRow
+ * @param y the y coordinate, relative to the ExpandableNotificationRow
+ * @return {@code true} if it is on the view
+ */
+ protected boolean isOnView(View view, float x, float y) {
+ View searchView = (View) view.getParent();
+ while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
+ searchView.getHitRect(mTmpRect);
+ x -= mTmpRect.left;
+ y -= mTmpRect.top;
+ searchView = (View) searchView.getParent();
+ }
+ view.getHitRect(mTmpRect);
+ return mTmpRect.contains((int) x,(int) y);
+ }
+
public int getMinLayoutHeight() {
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index d7c88e3..2c17764 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -412,7 +412,7 @@
* @param callback
* @return whether the list order has changed
*/
- public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
+ public boolean applyChildOrder(List<? extends NotificationListItem> childOrder,
VisualStabilityManager visualStabilityManager,
VisualStabilityManager.Callback callback) {
if (childOrder == null) {
@@ -421,7 +421,7 @@
boolean result = false;
for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
ExpandableNotificationRow child = mChildren.get(i);
- ExpandableNotificationRow desiredChild = childOrder.get(i);
+ ExpandableNotificationRow desiredChild = (ExpandableNotificationRow) childOrder.get(i);
if (child != desiredChild) {
if (visualStabilityManager.canReorderNotification(desiredChild)) {
mChildren.remove(desiredChild);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 15cc72c..c4a720c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -18,12 +18,14 @@
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import android.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.SimpleNotificationListContainer;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -33,7 +35,7 @@
* notification views added and removed from it, and will manage displaying them to the user.
*/
public interface NotificationListContainer extends ExpandableView.OnHeightChangedListener,
- VisibilityLocationProvider {
+ VisibilityLocationProvider, SimpleNotificationListContainer {
/**
* Called when a child is being transferred.
@@ -186,4 +188,10 @@
}
default void setWillExpand(boolean willExpand) {};
+
+ /**
+ * Remove a list item from the container
+ * @param v the item to remove
+ */
+ void removeListItem(@NonNull NotificationListItem v);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
new file mode 100644
index 0000000..8991abe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
@@ -0,0 +1,66 @@
+/*
+ * 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.stack;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.View;
+
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.List;
+
+/**
+* A NotificationListItem is a child view of the notification list that can yield a
+* NotificationEntry when asked. I.e., it's an ExpandableNotificationRow but doesn't require us
+* to strictly rely on ExpandableNotificationRow as our consumed type
+ */
+public interface NotificationListItem {
+ /** @return entry for this item */
+ @NonNull
+ NotificationEntry getEntry();
+
+ /** @return true if the blocking helper is showing */
+ boolean isBlockingHelperShowing();
+
+ /** @return true if this list item is a summary with children */
+ boolean isSummaryWithChildren();
+
+ // This generic is kind of ugly - we should change this once the old VHM is gone
+ /** @return list of the children of this item */
+ List<? extends NotificationListItem> getNotificationChildren();
+
+ /** remove all children from this list item */
+ void removeAllChildren();
+
+ /** remove particular child */
+ void removeChildNotification(NotificationListItem child);
+
+ /** add an item as a child */
+ void addChildNotification(NotificationListItem child, int childIndex);
+
+ /** Update the order of the children with the new list */
+ boolean applyChildOrder(
+ List<? extends NotificationListItem> childOrderList,
+ VisualStabilityManager vsm,
+ @Nullable VisualStabilityManager.Callback callback);
+
+ /** return the associated view for this list item */
+ @NonNull
+ View getView();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index cfcbd88..4d4a2ded 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3323,11 +3323,21 @@
}
@Override
+ public void notifyGroupChildRemoved(View child, ViewGroup parent) {
+ notifyGroupChildRemoved((ExpandableView) child, parent);
+ }
+
+ @Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void notifyGroupChildAdded(ExpandableView row) {
onViewAddedInternal(row);
}
+ @Override
+ public void notifyGroupChildAdded(View view) {
+ notifyGroupChildAdded((ExpandableView) view);
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setAnimationsEnabled(boolean animationsEnabled) {
mAnimationsEnabled = animationsEnabled;
@@ -5137,11 +5147,22 @@
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void removeListItem(NotificationListItem v) {
+ removeContainerView(v.getView());
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void addContainerView(View v) {
Assert.isMainThread();
addView(v);
}
+ @Override
+ public void addListItem(NotificationListItem v) {
+ addContainerView(v.getView());
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void runAfterAnimationFinished(Runnable runnable) {
mAnimationFinishedRunnables.add(runnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 0996ff2..14442e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -453,9 +453,10 @@
needsAnimation = false;
}
NotificationEntry entry = row.getEntry();
- StatusBarIconView icon = entry.icon;
- if (entry.centeredIcon != null && entry.centeredIcon.getParent() != null) {
- icon = entry.centeredIcon;
+ StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
+ final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
+ if (centeredIcon != null && centeredIcon.getParent() != null) {
+ icon = centeredIcon;
}
if (icon.getParent() != null) {
icon.getLocationOnScreen(mTmpLocation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 745843d..adca10f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -44,6 +44,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
@@ -99,8 +100,10 @@
private final Region mExcludeRegion = new Region();
private final Region mUnrestrictedExcludeRegion = new Region();
- // The edge width where touch down is allowed
- private int mEdgeWidth;
+ // The left side edge width where touch down is allowed
+ private int mEdgeWidthLeft;
+ // The right side edge width where touch down is allowed
+ private int mEdgeWidthRight;
// The bottom gesture area height
private int mBottomGestureHeight;
// The slop to distinguish between horizontal and vertical motion
@@ -127,6 +130,8 @@
private int mRightInset;
private int mSysUiFlags;
+ private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+
private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
new NavigationEdgeBackPlugin.BackCallback() {
@Override
@@ -174,13 +179,17 @@
mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
ViewConfiguration.getLongPressTimeout());
+ mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
+ mContext.getMainThreadHandler(), mContext, () -> updateCurrentUserResources(res));
+
updateCurrentUserResources(res);
sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
}
public void updateCurrentUserResources(Resources res) {
- mEdgeWidth = res.getDimensionPixelSize(
- com.android.internal.R.dimen.config_backGestureInset);
+ mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
+ mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
+
mBottomGestureHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_gesture_height);
}
@@ -236,6 +245,7 @@
}
if (!mIsEnabled) {
+ mGestureNavigationSettingsObserver.unregister();
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
mPluginManager.removePluginListener(this);
@@ -248,6 +258,7 @@
}
} else {
+ mGestureNavigationSettingsObserver.register();
updateDisplaySize();
mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
mContext.getMainThreadHandler());
@@ -321,7 +332,8 @@
private boolean isWithinTouchRegion(int x, int y) {
// Disallow if too far from the edge
- if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
+ if (x > mEdgeWidthLeft + mLeftInset
+ && x < (mDisplaySize.x - mEdgeWidthRight - mRightInset)) {
return false;
}
@@ -364,7 +376,7 @@
if (action == MotionEvent.ACTION_DOWN) {
// Verify if this is in within the touch region and we aren't in immersive mode, and
// either the bouncer is showing or the notification panel is hidden
- mIsOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
+ mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
mInRejectedExclusion = false;
mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
@@ -461,7 +473,8 @@
pw.println(" mExcludeRegion=" + mExcludeRegion);
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
pw.println(" mIsAttached=" + mIsAttached);
- pw.println(" mEdgeWidth=" + mEdgeWidth);
+ pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft);
+ pw.println(" mEdgeWidthRight=" + mEdgeWidthRight);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index c39ee3a..51c02c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -269,7 +269,7 @@
}
updateIsolatedIconLocation(false /* requireUpdate */);
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
- : newEntry.icon, animateIsolation);
+ : newEntry.getIcons().getStatusBarIcon(), animateIsolation);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index c282cb8..0b747f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -114,8 +114,9 @@
mInitialTouchY = y;
int startHeight = (int) (mPickedChild.getActualHeight()
+ mPickedChild.getTranslationY());
- mPanel.setPanelScrimMinFraction((float) startHeight
- / mPanel.getMaxPanelHeight());
+ float maxPanelHeight = mPanel.getMaxPanelHeight();
+ mPanel.setPanelScrimMinFraction(maxPanelHeight > 0f
+ ? (float) startHeight / maxPanelHeight : 0f);
mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
mPanel.startExpandingFromPeek();
// This call needs to be after the expansion start otherwise we will get a
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index ee31300..90bc075b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -492,7 +492,7 @@
try {
result = ActivityTaskManager.getService().startActivityAsUser(
null, getContext().getBasePackageName(),
- getContext().getFeatureId(), intent,
+ getContext().getAttributionTag(), intent,
intent.resolveTypeIfNeeded(getContext().getContentResolver()),
null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(),
UserHandle.CURRENT.getIdentifier());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index c5c3fff..c54fa29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -27,6 +27,8 @@
*/
public class KeyguardIndicationTextView extends TextView {
+ private CharSequence mText = "";
+
public KeyguardIndicationTextView(Context context) {
super(context);
}
@@ -53,10 +55,12 @@
// TODO: Animation, make sure that we will show one indication long enough.
if (TextUtils.isEmpty(text)) {
+ mText = "";
setVisibility(View.INVISIBLE);
- } else {
+ } else if (!TextUtils.equals(text, mText)) {
+ mText = text;
setVisibility(View.VISIBLE);
- setText(text);
+ setText(mText);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b119f0b..31266db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -551,14 +551,15 @@
public void setWindowState(
int displayId, @WindowType int window, @WindowVisibleState int state) {
if (displayId == mDisplayId
- && mNavigationBarView != null
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR
&& mNavigationBarWindowState != state) {
mNavigationBarWindowState = state;
+ updateSystemUiStateFlags(-1);
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
- updateSystemUiStateFlags(-1);
- mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+ if (mNavigationBarView != null) {
+ mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+ }
}
}
@@ -1219,6 +1220,7 @@
@Override
public void onViewDetachedFromWindow(View v) {
FragmentHostManager.removeAndDestroy(v);
+ navigationBarView.removeOnAttachStateChangeListener(this);
}
});
context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 43ac4cf..d24ccf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
+import android.content.om.OverlayInfo;
import android.content.pm.PackageManager;
import android.content.res.ApkAssets;
import android.os.PatternMatcher;
@@ -172,6 +173,9 @@
public void updateCurrentInteractionMode(boolean notify) {
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
+ if (mode == NAV_BAR_MODE_GESTURAL) {
+ switchToDefaultGestureNavOverlayIfNecessary();
+ }
mUiBgExecutor.execute(() -> {
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode));
@@ -277,6 +281,34 @@
}
}
+ private void switchToDefaultGestureNavOverlayIfNecessary() {
+ final int userId = mCurrentUserContext.getUserId();
+ try {
+ final IOverlayManager om = mOverlayManager;
+ final OverlayInfo info = om.getOverlayInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
+ if (info != null && !info.isEnabled()) {
+ // Enable the default gesture nav overlay, and move the back gesture inset scale to
+ // Settings.Secure for left and right sensitivity.
+ final int curInset = mCurrentUserContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+ om.setEnabledExclusiveInCategory(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
+ final int defInset = mCurrentUserContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+
+ final float scale = defInset == 0 ? 1.0f : ((float) curInset) / defInset;
+ Settings.Secure.putFloat(mCurrentUserContext.getContentResolver(),
+ Secure.BACK_GESTURE_INSET_SCALE_LEFT, scale);
+ Settings.Secure.putFloat(mCurrentUserContext.getContentResolver(),
+ Secure.BACK_GESTURE_INSET_SCALE_RIGHT, scale);
+ if (DEBUG) {
+ Log.v(TAG, "Moved back sensitivity for user " + userId + " to scale " + scale);
+ }
+ }
+ } catch (SecurityException | IllegalStateException | RemoteException e) {
+ Log.e(TAG, "Failed to switch to default gesture nav overlay for user " + userId);
+ }
+ }
+
public void setModeOverlay(String overlayPkg, int userId) {
mUiBgExecutor.execute(() -> {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
deleted file mode 100644
index 5d8044f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2008 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.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.annotation.IntDef;
-import android.content.ComponentCallbacks;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.Settings;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different
- * prototypes to run in the system. The class will handle communication changes from the settings
- * app and call back to listeners.
- */
-public class NavigationPrototypeController extends ContentObserver implements ComponentCallbacks {
- private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
- private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome";
- private static final String PROTOTYPE_ENABLED = "prototype_enabled";
-
- public static final String EDGE_SENSITIVITY_WIDTH_SETTING =
- "quickstepcontroller_edge_width_sensitivity";
- private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
- public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
- public static final String SHOW_HOME_HANDLE_SETTING = "quickstepcontroller_showhandle";
- public static final String ENABLE_ASSISTANT_GESTURE = "ENABLE_ASSISTANT_GESTURE";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK,
- ACTION_QUICKSWITCH, ACTION_NOTHING, ACTION_ASSISTANT})
- @interface GestureAction {}
- static final int ACTION_DEFAULT = 0;
- static final int ACTION_QUICKSTEP = 1;
- static final int ACTION_QUICKSCRUB = 2;
- static final int ACTION_BACK = 3;
- static final int ACTION_QUICKSWITCH = 4;
- static final int ACTION_NOTHING = 5;
- static final int ACTION_ASSISTANT = 6;
-
- private OnPrototypeChangedListener mListener;
-
- /**
- * Each index corresponds to a different action set in QuickStepController
- * {@see updateSwipeLTRBackSetting}
- */
- private int[] mActionMap = new int[6];
-
- private final Context mContext;
-
- public NavigationPrototypeController(Context context) {
- super(new Handler());
- mContext = context;
- updateSwipeLTRBackSetting();
- }
-
- public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) {
- mListener = listener;
- }
-
- /**
- * Observe all the settings to react to from prototype settings
- */
- public void register() {
- registerObserver(HIDE_BACK_BUTTON_SETTING);
- registerObserver(HIDE_HOME_BUTTON_SETTING);
- registerObserver(GESTURE_MATCH_SETTING);
- registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
- registerObserver(SHOW_HOME_HANDLE_SETTING);
- registerObserver(ENABLE_ASSISTANT_GESTURE);
- mContext.registerComponentCallbacks(this);
- }
-
- /**
- * Disable observing settings to react to from prototype settings
- */
- public void unregister() {
- mContext.getContentResolver().unregisterContentObserver(this);
- mContext.unregisterComponentCallbacks(this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- if (!selfChange && mListener != null) {
- final String path = uri.getPath();
- if (path.endsWith(GESTURE_MATCH_SETTING)) {
- // Get the settings gesture map corresponding to each action
- // {@see updateSwipeLTRBackSetting}
- updateSwipeLTRBackSetting();
- mListener.onGestureRemap(mActionMap);
- } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
- mListener.onBackButtonVisibilityChanged(
- !getGlobalBool(HIDE_BACK_BUTTON_SETTING, false));
- } else if (path.endsWith(HIDE_HOME_BUTTON_SETTING)) {
- mListener.onHomeButtonVisibilityChanged(!hideHomeButton());
- } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
- mListener.onColorAdaptChanged(mContext.getDisplayId() == DEFAULT_DISPLAY);
- } else if (path.endsWith(SHOW_HOME_HANDLE_SETTING)) {
- mListener.onHomeHandleVisiblilityChanged(showHomeHandle());
- } else if (path.endsWith(ENABLE_ASSISTANT_GESTURE)) {
- mListener.onAssistantGestureEnabled(isAssistantGestureEnabled());
- }
- }
- }
-
- /**
- * @return the width for edge swipe
- */
- public int getEdgeSensitivityWidth() {
- return mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.config_backGestureInset);
- }
-
- /**
- * @return full screen height
- */
- public int getEdgeSensitivityHeight() {
- final Point size = new Point();
- mContext.getDisplay().getRealSize(size);
- return size.y;
- }
-
- public boolean isEnabled() {
- return getGlobalBool(PROTOTYPE_ENABLED, false);
- }
-
- /**
- * Retrieve the action map to apply to the quick step controller
- * @return an action map
- */
- int[] getGestureActionMap() {
- return mActionMap;
- }
-
- /**
- * @return if home button should be invisible
- */
- boolean hideHomeButton() {
- return getGlobalBool(HIDE_HOME_BUTTON_SETTING, false /* default */);
- }
-
- boolean showHomeHandle() {
- return getGlobalBool(SHOW_HOME_HANDLE_SETTING, false /* default */);
- }
-
- boolean isAssistantGestureEnabled() {
- return getGlobalBool(ENABLE_ASSISTANT_GESTURE, false /* default */);
- }
-
-
- /**
- * Since Settings.Global cannot pass arrays, use a string to represent each character as a
- * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
- * Number: [up] [down] [left] [right]
- */
- private void updateSwipeLTRBackSetting() {
- String value = Settings.Global.getString(mContext.getContentResolver(),
- GESTURE_MATCH_SETTING);
- if (value != null) {
- for (int i = 0; i < mActionMap.length; ++i) {
- mActionMap[i] = Character.getNumericValue(value.charAt(i));
- }
- }
- }
-
- private boolean getGlobalBool(String name, boolean defaultVal) {
- return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal ? 1 : 0) == 1;
- }
-
- private int getGlobalInt(String name, int defaultVal) {
- return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal);
- }
-
- private void registerObserver(String name) {
- mContext.getContentResolver()
- .registerContentObserver(Settings.Global.getUriFor(name), false, this);
- }
-
- private static int convertDpToPixel(float dp) {
- return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- if (mListener != null) {
- mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(),
- getEdgeSensitivityHeight());
- }
- }
-
- @Override
- public void onLowMemory() {
- }
-
- public interface OnPrototypeChangedListener {
- void onGestureRemap(@GestureAction int[] actions);
- void onBackButtonVisibilityChanged(boolean visible);
- void onHomeButtonVisibilityChanged(boolean visible);
- void onHomeHandleVisiblilityChanged(boolean visible);
- void onColorAdaptChanged(boolean enabled);
- void onEdgeSensitivityChanged(int width, int height);
- void onAssistantGestureEnabled(boolean enabled);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 77337e9..ccf6707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -398,8 +398,7 @@
NotificationGroup group = mGroupMap.get(groupKey);
//TODO: see if this can become an Entry
return group == null ? null
- : group.summary == null ? null
- : group.summary;
+ : group.summary;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index b09ccff..f58cce5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -253,8 +253,8 @@
boolean hidePulsing, boolean onlyShowCenteredIcon) {
final boolean isCenteredNotificationIcon = mCenteredIconView != null
- && entry.centeredIcon != null
- && Objects.equals(entry.centeredIcon, mCenteredIconView);
+ && entry.getIcons().getCenteredIcon() != null
+ && Objects.equals(entry.getIcons().getCenteredIcon(), mCenteredIconView);
if (onlyShowCenteredIcon) {
return isCenteredNotificationIcon;
}
@@ -307,7 +307,7 @@
}
private void updateShelfIcons() {
- updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
+ updateIconsForLayout(entry -> entry.getIcons().getShelfIcon(), mShelfIcons,
true /* showAmbient */,
true /* showLowPriority */,
false /* hideDismissed */,
@@ -319,7 +319,7 @@
}
public void updateStatusBarIcons() {
- updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
+ updateIconsForLayout(entry -> entry.getIcons().getStatusBarIcon(), mNotificationIcons,
false /* showAmbient */,
mShowLowPriority,
true /* hideDismissed */,
@@ -331,7 +331,7 @@
}
private void updateCenterIcon() {
- updateIconsForLayout(entry -> entry.centeredIcon, mCenteredIcon,
+ updateIconsForLayout(entry -> entry.getIcons().getCenteredIcon(), mCenteredIcon,
false /* showAmbient */,
true /* showLowPriority */,
false /* hideDismissed */,
@@ -343,7 +343,7 @@
}
public void updateAodNotificationIcons() {
- updateIconsForLayout(entry -> entry.aodIcon, mAodIcons,
+ updateIconsForLayout(entry -> entry.getIcons().getAodIcon(), mAodIcons,
false /* showAmbient */,
true /* showLowPriority */,
true /* hideDismissed */,
@@ -517,7 +517,7 @@
* Shows the icon view given in the center.
*/
public void showIconCentered(NotificationEntry entry) {
- StatusBarIconView icon = entry == null ? null : entry.centeredIcon;
+ StatusBarIconView icon = entry == null ? null : entry.getIcons().getCenteredIcon();
if (!Objects.equals(mCenteredIconView, icon)) {
mCenteredIconView = icon;
updateNotificationIcons();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index c61d7bb..f9726d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1844,7 +1844,14 @@
} else {
maxHeight = calculatePanelHeightShade();
}
- maxHeight = Math.max(maxHeight, min);
+ maxHeight = Math.max(min, maxHeight);
+ if (maxHeight == 0) {
+ Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): "
+ + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: "
+ + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
+ + calculatePanelHeightShade() + ", mStatusBarMinHeight = "
+ + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight);
+ }
return maxHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 75f5023..e1e679f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -309,7 +309,8 @@
return !state.mForceCollapsed && (state.isKeyguardShowingAndNotOccluded()
|| state.mPanelVisible || state.mKeyguardFadingAway || state.mBouncerShowing
|| state.mHeadsUpShowing || state.mBubblesShowing
- || state.mScrimsVisibility != ScrimController.TRANSPARENT);
+ || state.mScrimsVisibility != ScrimController.TRANSPARENT)
+ || state.mBackgroundBlurRadius > 0;
}
private void applyFitsSystemWindows(State state) {
@@ -478,6 +479,19 @@
apply(mCurrentState);
}
+ /**
+ * Current blur level, controller by
+ * {@link com.android.systemui.statusbar.NotificationShadeDepthController}.
+ * @param backgroundBlurRadius Radius in pixels.
+ */
+ public void setBackgroundBlurRadius(int backgroundBlurRadius) {
+ if (mCurrentState.mBackgroundBlurRadius == backgroundBlurRadius) {
+ return;
+ }
+ mCurrentState.mBackgroundBlurRadius = backgroundBlurRadius;
+ apply(mCurrentState);
+ }
+
public void setHeadsUpShowing(boolean showing) {
mCurrentState.mHeadsUpShowing = showing;
apply(mCurrentState);
@@ -665,6 +679,7 @@
boolean mForcePluginOpen;
boolean mDozing;
int mScrimsVisibility;
+ int mBackgroundBlurRadius;
private boolean isKeyguardShowingAndNotOccluded() {
return mKeyguardShowing && !mKeyguardOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index f38d416..596a607 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -18,7 +18,6 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import android.annotation.Nullable;
import android.app.StatusBarManager;
import android.graphics.RectF;
import android.hardware.display.AmbientDisplayConfiguration;
@@ -44,7 +43,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -82,7 +81,7 @@
private final CommandQueue mCommandQueue;
private final NotificationShadeWindowView mView;
private final ShadeController mShadeController;
- private final NotificationShadeWindowBlurController mBlurController;
+ private final NotificationShadeDepthController mDepthController;
private GestureDetector mGestureDetector;
private View mBrightnessMirror;
@@ -126,7 +125,7 @@
CommandQueue commandQueue,
ShadeController shadeController,
DockManager dockManager,
- @Nullable NotificationShadeWindowBlurController blurController,
+ NotificationShadeDepthController depthController,
NotificationShadeWindowView notificationShadeWindowView,
NotificationPanelViewController notificationPanelViewController,
SuperStatusBarViewFactory statusBarViewFactory) {
@@ -149,7 +148,7 @@
mShadeController = shadeController;
mDockManager = dockManager;
mNotificationPanelViewController = notificationPanelViewController;
- mBlurController = blurController;
+ mDepthController = depthController;
mStatusBarViewFactory = statusBarViewFactory;
// This view is not part of the newly inflated expanded status bar.
@@ -394,10 +393,8 @@
mView.getContext(), mView, expandHelperCallback,
dragDownCallback, mFalsingManager));
- if (mBlurController != null) {
- mBlurController.setRoot(mView);
- mNotificationPanelViewController.addExpansionListener(mBlurController);
- }
+ mDepthController.setRoot(mView);
+ mNotificationPanelViewController.addExpansionListener(mDepthController);
}
public NotificationShadeWindowView getView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 8d8c8da..c106518 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static java.lang.Float.isNaN;
+
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
@@ -161,6 +163,9 @@
* fraction as the panel also might be expanded if the fraction is 0
*/
public void panelExpansionChanged(float frac, boolean expanded) {
+ if (isNaN(frac)) {
+ throw new IllegalArgumentException("frac cannot be NaN");
+ }
boolean fullyClosed = true;
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 1359f74..1c1e7c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -18,6 +18,8 @@
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+import static java.lang.Float.isNaN;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -40,6 +42,7 @@
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.leak.RotationUtils;
import java.util.Objects;
@@ -47,7 +50,6 @@
private static final String TAG = "PhoneStatusBarView";
private static final boolean DEBUG = StatusBar.DEBUG;
private static final boolean DEBUG_GESTURES = false;
- private static final int NO_VALUE = Integer.MIN_VALUE;
private final CommandQueue mCommandQueue;
StatusBar mBar;
@@ -64,13 +66,14 @@
}
};
private DarkReceiver mBattery;
- private int mLastOrientation;
+ private int mRotationOrientation;
@Nullable
private View mCenterIconSpace;
@Nullable
private View mCutoutSpace;
@Nullable
private DisplayCutout mDisplayCutout;
+ private int mStatusBarHeight;
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -107,7 +110,7 @@
super.onAttachedToWindow();
// Always have Battery meters in the status bar observe the dark/light modes.
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
- if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
+ if (updateOrientationAndCutout()) {
updateLayoutForCutout();
}
}
@@ -124,7 +127,7 @@
super.onConfigurationChanged(newConfig);
// May trigger cutout space layout-ing
- if (updateOrientationAndCutout(newConfig.orientation)) {
+ if (updateOrientationAndCutout()) {
updateLayoutForCutout();
requestLayout();
}
@@ -132,7 +135,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (updateOrientationAndCutout(mLastOrientation)) {
+ if (updateOrientationAndCutout()) {
updateLayoutForCutout();
requestLayout();
}
@@ -140,17 +143,14 @@
}
/**
- *
- * @param newOrientation may pass NO_VALUE for no change
* @return boolean indicating if we need to update the cutout location / margins
*/
- private boolean updateOrientationAndCutout(int newOrientation) {
+ private boolean updateOrientationAndCutout() {
boolean changed = false;
- if (newOrientation != NO_VALUE) {
- if (mLastOrientation != newOrientation) {
- changed = true;
- mLastOrientation = newOrientation;
- }
+ int newRotation = RotationUtils.getExactRotation(mContext);
+ if (newRotation != mRotationOrientation) {
+ changed = true;
+ mRotationOrientation = newRotation;
}
if (!Objects.equals(getRootWindowInsets().getDisplayCutout(), mDisplayCutout)) {
@@ -256,6 +256,9 @@
@Override
public void panelScrimMinFractionChanged(float minFraction) {
+ if (isNaN(minFraction)) {
+ throw new IllegalArgumentException("minFraction cannot be NaN");
+ }
if (mMinFraction != minFraction) {
mMinFraction = minFraction;
updateScrimFraction();
@@ -293,17 +296,16 @@
final int waterfallTopInset =
mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
ViewGroup.LayoutParams layoutParams = getLayoutParams();
- layoutParams.height =
- getResources().getDimensionPixelSize(R.dimen.status_bar_height) - waterfallTopInset;
+ mStatusBarHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_height);
+ layoutParams.height = mStatusBarHeight - waterfallTopInset;
setLayoutParams(layoutParams);
}
private void updateLayoutForCutout() {
updateStatusBarHeight();
- Pair<Integer, Integer> cornerCutoutMargins =
- StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
- updateCutoutLocation(cornerCutoutMargins);
- updateSafeInsets(cornerCutoutMargins);
+ updateCutoutLocation(StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay()));
+ updateSafeInsets(StatusBarWindowView.statusBarCornerCutoutMargins(mDisplayCutout,
+ getDisplay(), mRotationOrientation, mStatusBarHeight));
}
private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index d1ff32d..c590139 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -47,6 +47,7 @@
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -107,21 +108,21 @@
/**
* Default alpha value for most scrims.
*/
- public static final float SCRIM_ALPHA = 0.2f;
+ protected static final float KEYGUARD_SCRIM_ALPHA = 0.2f;
/**
* Scrim opacity when the phone is about to wake-up.
*/
public static final float WAKE_SENSOR_SCRIM_ALPHA = 0.6f;
/**
- * A scrim varies its opacity based on a busyness factor, for example
- * how many notifications are currently visible.
+ * The default scrim under the shade and dialogs.
+ * This should not be lower than 0.54, otherwise we won't pass GAR.
*/
public static final float BUSY_SCRIM_ALPHA = 0.75f;
/**
- * The most common scrim, the one under the keyguard.
+ * Same as above, but when blur is supported.
*/
- protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = SCRIM_ALPHA;
+ public static final float BLUR_SCRIM_ALPHA = 0.54f;
static final int TAG_KEY_ANIM = R.id.scrim;
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
@@ -146,7 +147,8 @@
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
- private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
+ private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
+ private final float mDefaultScrimAlpha;
// Assuming the shade is expanded during initialization
private float mExpansionFraction = 1f;
@@ -192,9 +194,11 @@
AlarmManager alarmManager, KeyguardStateController keyguardStateController,
DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor,
- DockManager dockManager) {
+ DockManager dockManager, BlurUtils blurUtils) {
mScrimStateListener = lightBarController::setScrimState;
+ mDefaultScrimAlpha = blurUtils.supportsBlursOnWindows()
+ ? BLUR_SCRIM_ALPHA : BUSY_SCRIM_ALPHA;
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -236,6 +240,7 @@
states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters,
mDockManager);
states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
+ states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
}
mScrimBehind.setDefaultFocusHighlightEnabled(false);
@@ -483,7 +488,7 @@
// Darken scrim as you pull down the shade when unlocked
float behindFraction = getInterpolatedFraction();
behindFraction = (float) Math.pow(behindFraction, 0.8f);
- mBehindAlpha = behindFraction * BUSY_SCRIM_ALPHA;
+ mBehindAlpha = behindFraction * mDefaultScrimAlpha;
mInFrontAlpha = 0;
} else if (mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING) {
// Either darken of make the scrim transparent when you
@@ -491,7 +496,7 @@
float interpolatedFract = getInterpolatedFraction();
float alphaBehind = mState.getBehindAlpha();
if (mDarkenWhileDragging) {
- mBehindAlpha = MathUtils.lerp(BUSY_SCRIM_ALPHA, alphaBehind,
+ mBehindAlpha = MathUtils.lerp(mDefaultScrimAlpha, alphaBehind,
interpolatedFract);
mInFrontAlpha = mState.getFrontAlpha();
} else {
@@ -967,7 +972,8 @@
pw.print(" mTracking=");
pw.println(mTracking);
-
+ pw.print(" mDefaultScrimAlpha=");
+ pw.println(mDefaultScrimAlpha);
pw.print(" mExpansionFraction=");
pw.println(mExpansionFraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index c23fd0a..ade642c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -92,7 +92,7 @@
BOUNCER {
@Override
public void prepare(ScrimState previousState) {
- mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mBehindAlpha = mDefaultScrimAlpha;
mFrontAlpha = 0f;
mBubbleAlpha = 0f;
}
@@ -106,7 +106,7 @@
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
mBubbleAlpha = 0f;
- mFrontAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mFrontAlpha = mDefaultScrimAlpha;
}
},
@@ -233,9 +233,9 @@
mBehindTint = Color.TRANSPARENT;
mBubbleTint = Color.TRANSPARENT;
- mFrontAlpha = ScrimController.TRANSPARENT;
- mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA;
- mBubbleAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mFrontAlpha = 0f;
+ mBehindAlpha = mDefaultScrimAlpha;
+ mBubbleAlpha = mDefaultScrimAlpha;
mAnimationDuration = ScrimController.ANIMATION_DURATION;
mBlankScreen = false;
@@ -255,6 +255,7 @@
float mBubbleAlpha;
float mScrimBehindAlphaKeyguard;
+ float mDefaultScrimAlpha;
ScrimView mScrimInFront;
ScrimView mScrimBehind;
ScrimView mScrimForBubble;
@@ -341,6 +342,10 @@
mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
}
+ public void setDefaultScrimAlpha(float defaultScrimAlpha) {
+ mDefaultScrimAlpha = defaultScrimAlpha;
+ }
+
public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
}
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 b3a62d8..d343090 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -185,15 +185,15 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -404,10 +404,9 @@
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final KeyguardViewMediator mKeyguardViewMediator;
- private final NotificationAlertingManager mNotificationAlertingManager;
+ protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
// for disabling the status bar
private int mDisabled1 = 0;
@@ -621,10 +620,10 @@
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
- NotificationAlertingManager notificationAlertingManager,
+ NotificationAlertingManager notificationAlertingManager, // need to inject for now
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
@UiBackground Executor uiBgExecutor,
@@ -701,10 +700,9 @@
mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mViewHierarchyManager = notificationViewHierarchyManager;
mKeyguardViewMediator = keyguardViewMediator;
- mNotificationAlertingManager = notificationAlertingManager;
mDisplayMetrics = displayMetrics;
mMetricsLogger = metricsLogger;
mUiBgExecutor = uiBgExecutor;
@@ -1238,9 +1236,9 @@
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
- mNotificationAlertingManager, mKeyguardStateController,
- mKeyguardIndicationController,
- this /* statusBar */, mShadeController, mCommandQueue, mInitController);
+ mKeyguardStateController, mKeyguardIndicationController,
+ this /* statusBar */, mShadeController, mCommandQueue, mInitController,
+ mNotificationInterruptStateProvider);
mNotificationShelf.setOnActivatedListener(mPresenter);
mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
@@ -1589,8 +1587,9 @@
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
- (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
+ if (areNotificationAlertsDisabled()) {
+ mHeadsUpManager.releaseAllImmediately();
+ }
}
if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
@@ -1605,6 +1604,10 @@
}
}
+ boolean areNotificationAlertsDisabled() {
+ return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
+ }
+
protected H createHandler() {
return new StatusBar.H();
}
@@ -2599,7 +2602,7 @@
}
try {
result = ActivityTaskManager.getService().startActivityAsUser(
- null, mContext.getBasePackageName(), mContext.getFeatureId(),
+ null, mContext.getBasePackageName(), mContext.getAttributionTag(),
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index e1a20b6..53fa263 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -68,12 +68,12 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -108,7 +108,7 @@
private final NotifCollection mNotifCollection;
private final FeatureFlags mFeatureFlags;
private final StatusBarStateController mStatusBarStateController;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final MetricsLogger mMetricsLogger;
private final Context mContext;
private final NotificationPanelViewController mNotificationPanel;
@@ -142,7 +142,7 @@
NotificationLockscreenUserManager lockscreenUserManager,
ShadeController shadeController, StatusBar statusBar,
KeyguardStateController keyguardStateController,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils,
Handler mainThreadHandler, Handler backgroundHandler, Executor uiBgExecutor,
ActivityIntentHelper activityIntentHelper, BubbleController bubbleController,
@@ -167,7 +167,7 @@
mActivityStarter = activityStarter;
mEntryManager = entryManager;
mStatusBarStateController = statusBarStateController;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
mGroupManager = groupManager;
@@ -436,7 +436,7 @@
}
private void handleFullScreenIntent(NotificationEntry entry) {
- if (mNotificationInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
+ if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
if (shouldSuppressFullScreenIntent(entry)) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.getKey());
@@ -603,7 +603,7 @@
private final ActivityIntentHelper mActivityIntentHelper;
private final BubbleController mBubbleController;
private NotificationPanelViewController mNotificationPanelViewController;
- private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final ShadeController mShadeController;
private NotificationPresenter mNotificationPresenter;
private ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -626,7 +626,7 @@
NotificationGroupManager groupManager,
NotificationLockscreenUserManager lockscreenUserManager,
KeyguardStateController keyguardStateController,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
@Main Handler mainThreadHandler,
@@ -654,7 +654,7 @@
mGroupManager = groupManager;
mLockscreenUserManager = lockscreenUserManager;
mKeyguardStateController = keyguardStateController;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mMetricsLogger = metricsLogger;
mLockPatternUtils = lockPatternUtils;
mMainThreadHandler = mainThreadHandler;
@@ -712,7 +712,7 @@
mShadeController,
mStatusBar,
mKeyguardStateController,
- mNotificationInterruptionStateProvider,
+ mNotificationInterruptStateProvider,
mMetricsLogger,
mLockPatternUtils,
mMainThreadHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 30d6b507..79cea91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -60,13 +60,13 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -98,8 +98,6 @@
(SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
- Dependency.get(NotificationInterruptionStateProvider.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
private final VisualStabilityManager mVisualStabilityManager =
@@ -140,13 +138,13 @@
ScrimController scrimController,
ActivityLaunchAnimator activityLaunchAnimator,
DynamicPrivacyController dynamicPrivacyController,
- NotificationAlertingManager notificationAlertingManager,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
StatusBar statusBar,
ShadeController shadeController,
CommandQueue commandQueue,
- InitController initController) {
+ InitController initController,
+ NotificationInterruptStateProvider notificationInterruptStateProvider) {
mContext = context;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
@@ -216,8 +214,7 @@
mEntryManager.addNotificationLifetimeExtender(mGutsManager);
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
- mNotificationInterruptionStateProvider.setUpWithPresenter(
- this, mHeadsUpManager, this::canHeadsUp);
+ notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
mVisualStabilityManager.setUpWithPresenter(this);
@@ -336,39 +333,6 @@
return mEntryManager.hasActiveNotifications();
}
- public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
- if (mStatusBar.isOccluded()) {
- boolean devicePublic = mLockscreenUserManager.
- isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
- boolean userPublic = devicePublic
- || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
- boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
- if (userPublic && needsRedaction) {
- // TODO(b/135046837): we can probably relax this with dynamic privacy
- return false;
- }
- }
-
- if (!mCommandQueue.panelsEnabled()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
- }
- return false;
- }
-
- if (sbn.getNotification().fullScreenIntent != null) {
- if (mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
- return false;
- } else {
- // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
- return !mKeyguardStateController.isShowing()
- || mStatusBar.isOccluded();
- }
- }
- return true;
- }
-
@Override
public void onUserSwitched(int newUserId) {
// Begin old BaseStatusBar.userSwitched
@@ -507,4 +471,66 @@
}
}
};
+
+ private final NotificationInterruptSuppressor mInterruptSuppressor =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+ final StatusBarNotification sbn = entry.getSbn();
+ if (mStatusBar.isOccluded()) {
+ boolean devicePublic = mLockscreenUserManager
+ .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
+ boolean userPublic = devicePublic
+ || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
+ boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
+ if (userPublic && needsRedaction) {
+ // TODO(b/135046837): we can probably relax this with dynamic privacy
+ return true;
+ }
+ }
+
+ if (!mCommandQueue.panelsEnabled()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
+ }
+ return true;
+ }
+
+ if (sbn.getNotification().fullScreenIntent != null) {
+ // we don't allow head-up on the lockscreen (unless there's a
+ // "showWhenLocked" activity currently showing) if
+ // the potential HUN has a fullscreen intent
+ if (mKeyguardStateController.isShowing() && !mStatusBar.isOccluded()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: entry has fullscreen intent on lockscreen "
+ + sbn.getKey());
+ }
+ return true;
+ }
+
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean suppressAwakeInterruptions(NotificationEntry entry) {
+ return isDeviceInVrMode();
+ }
+
+ @Override
+ public boolean suppressInterruptions(NotificationEntry entry) {
+ return mStatusBar.areNotificationAlertsDisabled();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 22bf513..760a6d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -33,6 +33,8 @@
import android.view.WindowInsets;
import android.widget.FrameLayout;
+import com.android.systemui.util.leak.RotationUtils;
+
/**
* Status bar view.
*/
@@ -52,17 +54,13 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
- mLeftInset = 0;
- mRightInset = 0;
+ mLeftInset = insets.left;
+ mRightInset = insets.right;
mTopInset = 0;
DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
if (displayCutout != null) {
mTopInset = displayCutout.getWaterfallInsets().top;
- mLeftInset = displayCutout.getSafeInsetLeft();
- mRightInset = displayCutout.getSafeInsetRight();
}
- mLeftInset = Math.max(insets.left, mLeftInset);
- mRightInset = Math.max(insets.right, mRightInset);
applyMargins();
return windowInsets;
}
@@ -100,44 +98,33 @@
return new Pair<>(roundedCornerContentPadding, roundedCornerContentPadding);
}
- // compute the padding needed for corner cutout.
- final int leftMargin = cutout.getSafeInsetLeft();
- final int rightMargin = cutout.getSafeInsetRight();
+ // padding needed for corner cutout.
int leftCornerCutoutPadding = 0;
int rightCornerCutoutPadding = 0;
if (cornerCutoutPadding != null) {
- if (cornerCutoutPadding.first > leftMargin) {
- leftCornerCutoutPadding = cornerCutoutPadding.first - leftMargin;
- }
- if (cornerCutoutPadding.second > rightMargin) {
- rightCornerCutoutPadding = cornerCutoutPadding.second - rightMargin;
- }
- }
-
- // compute the padding needed for rounded corner
- int leftRoundedCornerPadding = 0;
- int rightRoundedCornerPadding = 0;
- if (roundedCornerContentPadding > leftMargin) {
- leftRoundedCornerPadding = roundedCornerContentPadding - leftMargin;
- }
- if (roundedCornerContentPadding > rightMargin) {
- rightRoundedCornerPadding = roundedCornerContentPadding - rightMargin;
+ leftCornerCutoutPadding = cornerCutoutPadding.first;
+ rightCornerCutoutPadding = cornerCutoutPadding.second;
}
return new Pair<>(
- Math.max(leftCornerCutoutPadding, leftRoundedCornerPadding),
- Math.max(rightCornerCutoutPadding, rightRoundedCornerPadding));
+ Math.max(leftCornerCutoutPadding, roundedCornerContentPadding),
+ Math.max(rightCornerCutoutPadding, roundedCornerContentPadding));
}
+
/**
- * Compute the corner cutout margins
- *
- * @param cutout
- * @param display
- * @return
+ * Compute the corner cutout margins in portrait mode
*/
public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
Display display) {
+ return statusBarCornerCutoutMargins(cutout, display, RotationUtils.ROTATION_NONE, 0);
+ }
+
+ /**
+ * Compute the corner cutout margins in the given orientation (exactRotation)
+ */
+ public static Pair<Integer, Integer> statusBarCornerCutoutMargins(DisplayCutout cutout,
+ Display display, int exactRotation, int statusBarHeight) {
if (cutout == null) {
return null;
}
@@ -145,14 +132,33 @@
display.getRealSize(size);
Rect bounds = new Rect();
- boundsFromDirection(cutout, Gravity.TOP, bounds);
+ switch (exactRotation) {
+ case RotationUtils.ROTATION_LANDSCAPE:
+ boundsFromDirection(cutout, Gravity.LEFT, bounds);
+ break;
+ case RotationUtils.ROTATION_SEASCAPE:
+ boundsFromDirection(cutout, Gravity.RIGHT, bounds);
+ break;
+ case RotationUtils.ROTATION_NONE:
+ boundsFromDirection(cutout, Gravity.TOP, bounds);
+ break;
+ case RotationUtils.ROTATION_UPSIDE_DOWN:
+ // we assume the cutout is always on top in portrait mode
+ return null;
+ }
+
+ if (statusBarHeight >= 0 && bounds.top > statusBarHeight) {
+ return null;
+ }
if (bounds.left <= 0) {
return new Pair<>(bounds.right, 0);
}
+
if (bounds.right >= size.x) {
return new Pair<>(0, size.x - bounds.left);
}
+
return null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index eec8d50..bbc7e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -56,13 +56,13 @@
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -139,7 +139,7 @@
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
NotificationAlertingManager notificationAlertingManager,
@@ -218,7 +218,7 @@
remoteInputQuickSettingsDisabler,
notificationGutsManager,
notificationLogger,
- notificationInterruptionStateProvider,
+ notificationInterruptStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
notificationAlertingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 24b9685..a81189e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -53,6 +53,16 @@
boolean isAodPowerSave();
/**
+ * Returns {@code true} if reverse is supported.
+ */
+ default boolean isReverseSupported() { return false; }
+
+ /**
+ * Returns {@code true} if reverse is on.
+ */
+ default boolean isReverseOn() { return false; }
+
+ /**
* Set reverse state.
* @param isReverse true if turn on reverse, false otherwise
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 35954d8..496bf68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -99,7 +99,6 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
- filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
filter.addAction(ACTION_LEVEL_TEST);
mBroadcastDispatcher.registerReceiver(this, filter);
}
@@ -155,8 +154,6 @@
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
- } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
- setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
} else if (action.equals(ACTION_LEVEL_TEST)) {
mTestmode = true;
mMainHandler.post(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 759bad4..812ce1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -236,7 +236,7 @@
String action = intent.getAction();
if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- String tz = intent.getStringExtra("time-zone");
+ String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
handler.post(() -> {
mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
if (mClockFormat != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 7d532a8..38c165b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -65,7 +65,6 @@
new TetheringManager.TetheringEventCallback() {
@Override
public void onTetheringSupported(boolean supported) {
- super.onTetheringSupported(supported);
if (mIsTetheringSupported != supported) {
mIsTetheringSupported = supported;
fireHotspotAvailabilityChanged();
@@ -75,7 +74,6 @@
@Override
public void onTetherableInterfaceRegexpsChanged(
TetheringManager.TetheringInterfaceRegexps reg) {
- super.onTetherableInterfaceRegexpsChanged(reg);
final boolean newValue = reg.getTetherableWifiRegexs().size() != 0;
if (mHasTetherableWifiRegexs != newValue) {
mHasTetherableWifiRegexs = newValue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index cebcf76..54e8e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -577,7 +577,7 @@
}
boolean isDataDisabled() {
- return !mPhone.isDataConnectionEnabled();
+ return !mPhone.isDataConnectionAllowed();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index b84208c..99709402 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -630,7 +630,7 @@
@VisibleForTesting
void doUpdateMobileControllers() {
List<SubscriptionInfo> subscriptions = mSubscriptionManager
- .getActiveAndHiddenSubscriptionInfoList();
+ .getCompleteActiveSubscriptionInfoList();
if (subscriptions == null) {
subscriptions = Collections.emptyList();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
index 73ffe42..e70e30a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
@@ -24,6 +24,7 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.annotation.IntDef;
+import android.annotation.UiThread;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -133,6 +134,7 @@
new OnActiveRecordingListener());
}
+ @UiThread
private void onStartedRecording(String packageName) {
if (!mActiveAudioRecordingPackages.add(packageName)) {
// This app is already known to perform recording
@@ -164,6 +166,7 @@
}
}
+ @UiThread
private void onDoneRecording(String packageName) {
if (!mActiveAudioRecordingPackages.remove(packageName)) {
// Was not marked as an active recorder, do nothing
@@ -179,6 +182,7 @@
}
}
+ @UiThread
private void show(String packageName) {
// Inflate the indicator view
mIndicatorView = LayoutInflater.from(mContext).inflate(
@@ -253,6 +257,7 @@
mState = STATE_APPEARING;
}
+ @UiThread
private void expand(String packageName) {
final String label = getApplicationLabel(packageName);
mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
@@ -276,6 +281,7 @@
mState = STATE_MAXIMIZING;
}
+ @UiThread
private void minimize() {
final int targetOffset = mTextsContainers.getWidth();
final AnimatorSet set = new AnimatorSet();
@@ -297,6 +303,7 @@
mState = STATE_MINIMIZING;
}
+ @UiThread
private void hide() {
final int targetOffset =
mIndicatorView.getWidth() - (int) mIconTextsContainer.getTranslationX();
@@ -317,12 +324,14 @@
mState = STATE_DISAPPEARING;
}
+ @UiThread
private void onExpanded() {
mState = STATE_SHOWN;
mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION);
}
+ @UiThread
private void onMinimized() {
mState = STATE_MINIMIZED;
@@ -336,6 +345,7 @@
}
}
+ @UiThread
private void onHidden() {
final WindowManager windowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
@@ -358,6 +368,7 @@
}
}
+ @UiThread
private void startPulsatingAnimation() {
final View pulsatingView = mIconTextsContainer.findViewById(R.id.pulsating_circle);
final ObjectAnimator animator =
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 2f1b160..5411839 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -49,6 +49,8 @@
public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
+ private static final boolean DEBUG = false;
+
public static final int ANIMATION_DURATION_SHOW_MS = 275;
public static final int ANIMATION_DURATION_HIDE_MS = 340;
public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
@@ -209,6 +211,7 @@
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
+ if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
startAnimation(true /* show */);
}
@@ -217,6 +220,7 @@
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
+ if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
startAnimation(false /* show */);
}
@@ -241,6 +245,11 @@
return;
}
mHandler.post(() -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Run startAnim show:" + show + " was:"
+ + (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
+ : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
+ }
if ((mAnimationDirection == DIRECTION_SHOW && show)
|| (mAnimationDirection == DIRECTION_HIDE && !show)) {
return;
@@ -289,6 +298,11 @@
public void onAnimationStart(Animator animation) {
SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setPosition(mImeSourceControl.getLeash(), x, startY);
+ if (DEBUG) {
+ Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+ + imeTop(imeSource, hiddenY) + "->" + imeTop(imeSource, shownY)
+ + " showing:" + (mAnimationDirection == DIRECTION_SHOW));
+ }
dispatchStartPositioning(mDisplayId, imeTop(imeSource, hiddenY),
imeTop(imeSource, shownY), mAnimationDirection == DIRECTION_SHOW,
t);
@@ -304,6 +318,7 @@
}
@Override
public void onAnimationEnd(Animator animation) {
+ if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
SurfaceControl.Transaction t = mTransactionPool.acquire();
if (!mCancelled) {
t.setPosition(mImeSourceControl.getLeash(), x, endY);
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 23df991..381ccdb 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -196,7 +196,7 @@
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot = new SurfaceControlViewHost(mContext, display, wwm);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
- viewRoot.addView(view, attrs);
+ viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
}
@@ -324,7 +324,7 @@
@Override
public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
- boolean sync) {}
+ float zoom, boolean sync) {}
@Override
public void dispatchWallpaperCommand(String action, int x, int y,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index ea6cf33..f7daf97 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -249,7 +249,7 @@
// STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
// same answer as KeyguardUpdateMonitor. Remove when this is addressed
- when(mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList()).thenReturn(
+ when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(
new ArrayList<>());
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index eead120..4f4ce13 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -516,7 +516,7 @@
List<SubscriptionInfo> list = new ArrayList<>();
list.add(TEST_SUBSCRIPTION);
list.add(TEST_SUBSCRIPTION_2);
- when(mSubscriptionManager.getActiveAndHiddenSubscriptionInfoList()).thenReturn(list);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list);
mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged(
TEST_SUBSCRIPTION_2.getSubscriptionId());
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 1b34b3d..b6537bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -23,6 +23,8 @@
import static com.android.systemui.ScreenDecorations.rectsToRegion;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -42,6 +44,7 @@
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.drawable.VectorDrawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
@@ -142,6 +145,8 @@
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -161,6 +166,8 @@
com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -182,6 +189,67 @@
}
@Test
+ public void testRoundingRadius_NoCutout() {
+ final int testRadius = 1;
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, testRadius);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, testRadius);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, testRadius);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
+ // no cutout
+ doReturn(null).when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Size of corner view should same as rounded_corner_radius{_top|_bottom}
+ assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(testRadius);
+ assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(testRadius);
+ assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(testRadius);
+ }
+
+ @Test
+ public void testRoundingMultipleRadius_NoCutout() {
+ final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+ final int multipleRadiusSize = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 9999);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 9999);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, true);
+
+ // no cutout
+ doReturn(null).when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Top and bottom windows are created for rounded corners.
+ verify(mWindowManager, times(1))
+ .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
+ verify(mWindowManager, times(1))
+ .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
+
+ // Left and right window should be null.
+ assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
+ assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+
+ // One tunable.
+ verify(mTunerService, times(1)).addTunable(any(), any());
+
+ // Size of corner view should exactly match max(width, height) of R.drawable.rounded
+ assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(multipleRadiusSize);
+ assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(multipleRadiusSize);
+ assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(multipleRadiusSize);
+ }
+
+ @Test
public void testNoRounding_CutoutShortEdge() {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
@@ -193,6 +261,8 @@
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// top cutout
doReturn(new DisplayCutout(
@@ -227,6 +297,8 @@
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// left cutout
doReturn(new DisplayCutout(
@@ -257,6 +329,8 @@
com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// top cutout
doReturn(new DisplayCutout(
@@ -288,6 +362,8 @@
com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// left cutout
doReturn(new DisplayCutout(
@@ -319,6 +395,8 @@
com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// top and left cutout
doReturn(new DisplayCutout(
@@ -355,6 +433,8 @@
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// Set to short edge cutout(top).
doReturn(new DisplayCutout(
@@ -402,6 +482,8 @@
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// top cutout
doReturn(new DisplayCutout(
@@ -440,6 +522,8 @@
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.dimen.rounded_corner_radius, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
mScreenDecorations.start();
assertEquals(mScreenDecorations.mRoundedDefault, 20);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index a36f2c7..bb2eea9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -31,6 +31,7 @@
import androidx.test.InstrumentationRegistry;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.FalsingManager;
@@ -80,6 +81,9 @@
// None of them actually need it.
mDependency.injectTestDependency(FalsingManager.class, new FalsingManagerFake());
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+
+ // TODO: b/151614195 investigate root cause of needing this mock dependency
+ mDependency.injectMockDependency(LocalBluetoothManager.class);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index c6c7b87..1db8e4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -78,7 +78,9 @@
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_AUTHENTICATED);
- verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED));
+ verify(mCallback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
+ eq(null) /* credentialAttestation */);
}
@Test
@@ -87,7 +89,9 @@
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_USER_CANCELED);
- verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_USER_CANCELED));
+ verify(mCallback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+ eq(null) /* credentialAttestation */);
}
@Test
@@ -96,7 +100,9 @@
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE);
- verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE));
+ verify(mCallback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
+ eq(null) /* credentialAttestation */);
}
@Test
@@ -114,7 +120,9 @@
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_ERROR);
- verify(mCallback).onDismissed(AuthDialogCallback.DISMISSED_ERROR);
+ verify(mCallback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_ERROR),
+ eq(null) /* credentialAttestation */);
}
@Test
@@ -219,7 +227,8 @@
@Override
public void animateAway(int reason) {
- mConfig.mCallback.onDismissed(reason);
+ // TODO: Credential attestation should be testable/tested
+ mConfig.mCallback.onDismissed(reason, null /* credentialAttestation */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 65399bf..fc1ddf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -57,12 +57,14 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+import java.util.Random;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -110,52 +112,75 @@
@Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_NEGATIVE),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED,
+ null /* credentialAttestation */);
verify(mReceiver).onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
+ eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonError_whenDismissedByError() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_ERROR),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER,
+ null /* credentialAttestation */);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated()
throws Exception {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+
+ final byte[] credentialAttestation = generateRandomHAT();
+
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED,
+ credentialAttestation);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
+ AdditionalMatchers.aryEq(credentialAttestation));
}
// Statusbar tests
@@ -302,8 +327,13 @@
showDialog(Authenticators.DEVICE_CREDENTIAL, BiometricPrompt.TYPE_NONE);
verify(mDialog1).show(any(), any());
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+ final byte[] credentialAttestation = generateRandomHAT();
+
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED,
+ credentialAttestation);
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
+ AdditionalMatchers.aryEq(credentialAttestation));
mAuthController.hideAuthenticationDialog();
}
@@ -395,20 +425,24 @@
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
verify(mDialog1).dismissWithoutCallback(true /* animate */);
- verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
+ eq(null) /* credentialAttestation */);
}
@Test
public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
+ null /* credentialAttestation */);
mAuthController.onTryAgainPressed();
}
@Test
public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() {
showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
- mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
+ null /* credentialAttestation */);
mAuthController.onDeviceCredentialPressed();
}
@@ -422,7 +456,9 @@
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
verify(mDialog1).dismissWithoutCallback(true /* animate */);
- verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
+ verify(mReceiver).onDialogDismissed(
+ eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
+ eq(null) /* credentialAttestation */);
}
// Helpers
@@ -433,7 +469,8 @@
biometricModality,
true /* requireConfirmation */,
0 /* userId */,
- "testPackage");
+ "testPackage",
+ 0 /* operationId */);
}
private Bundle createTestDialogBundle(int authenticators) {
@@ -453,6 +490,13 @@
return bundle;
}
+ private byte[] generateRandomHAT() {
+ byte[] HAT = new byte[69];
+ Random random = new Random();
+ random.nextBytes(HAT);
+ return HAT;
+ }
+
private final class TestableAuthController extends AuthController {
private int mBuildCount = 0;
private Bundle mLastBiometricPromptBundle;
@@ -464,7 +508,7 @@
@Override
protected AuthDialog buildDialog(Bundle biometricPromptBundle,
boolean requireConfirmation, int userId, int type, String opPackageName,
- boolean skipIntro) {
+ boolean skipIntro, long operationId) {
mLastBiometricPromptBundle = biometricPromptBundle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 78160c4..6e612d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -45,7 +45,11 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.res.Resources;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.face.FaceManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -61,14 +65,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -227,15 +229,17 @@
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
- TestableNotificationInterruptionStateProvider interruptionStateProvider =
- new TestableNotificationInterruptionStateProvider(mContext,
+ TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
+ new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+ mock(PowerManager.class),
+ mock(IDreamManager.class),
+ mock(AmbientDisplayConfiguration.class),
mock(NotificationFilter.class),
mock(StatusBarStateController.class),
- mock(BatteryController.class));
- interruptionStateProvider.setUpWithPresenter(
- mock(NotificationPresenter.class),
- mock(HeadsUpManager.class),
- mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
+ mock(BatteryController.class),
+ mock(HeadsUpManager.class),
+ mock(Handler.class)
+ );
mBubbleData = new BubbleData(mContext);
when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mBubbleController = new TestableBubbleController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index f40fc94..866dfdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -268,13 +268,18 @@
sendUpdatedEntryAtTime(mEntryB2, 5000);
mBubbleData.setListener(mListener);
- // Test
sendUpdatedEntryAtTime(mEntryC1, 6000);
verifyUpdateReceived();
-
- // Verify
assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED);
- assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(mBubbleA1));
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
+
+ Bubble bubbleA1 = mBubbleData.getOrCreateBubble(mEntryA1);
+ bubbleA1.markUpdatedAt(7000L);
+ mBubbleData.notificationEntryUpdated(bubbleA1, false /* suppressFlyout*/,
+ true /* showInShade */);
+ verifyUpdateReceived();
+ assertBubbleRemoved(mBubbleA2, BubbleController.DISMISS_AGED);
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
/**
@@ -931,6 +936,11 @@
assertThat(update.expanded).named("expanded").isEqualTo(expected);
}
+ private void assertOverflowChangedTo(ImmutableList<Bubble> bubbles) {
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.overflowBubbles).isEqualTo(bubbles);
+ }
+
private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
return createBubbleEntry(userId, notifKey, packageName, 1000);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 5ef4cd2..6244644 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -41,7 +41,11 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.res.Resources;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.face.FaceManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -57,12 +61,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -212,15 +214,17 @@
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
- TestableNotificationInterruptionStateProvider interruptionStateProvider =
- new TestableNotificationInterruptionStateProvider(mContext,
+ TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
+ new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+ mock(PowerManager.class),
+ mock(IDreamManager.class),
+ mock(AmbientDisplayConfiguration.class),
mock(NotificationFilter.class),
mock(StatusBarStateController.class),
- mock(BatteryController.class));
- interruptionStateProvider.setUpWithPresenter(
- mock(NotificationPresenter.class),
- mock(HeadsUpManager.class),
- mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
+ mock(BatteryController.class),
+ mock(HeadsUpManager.class),
+ mock(Handler.class)
+ );
mBubbleData = new BubbleData(mContext);
when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
mBubbleController = new TestableBubbleController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index de1fb41..d3d90c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -23,8 +23,8 @@
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -44,7 +44,7 @@
ShadeController shadeController,
BubbleData data,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
+ NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java
new file mode 100644
index 0000000..17dc76b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.content.ContentResolver;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+public class TestableNotificationInterruptStateProviderImpl
+ extends NotificationInterruptStateProviderImpl {
+
+ TestableNotificationInterruptStateProviderImpl(
+ ContentResolver contentResolver,
+ PowerManager powerManager,
+ IDreamManager dreamManager,
+ AmbientDisplayConfiguration ambientDisplayConfiguration,
+ NotificationFilter filter,
+ StatusBarStateController statusBarStateController,
+ BatteryController batteryController,
+ HeadsUpManager headsUpManager,
+ Handler mainHandler) {
+ super(contentResolver,
+ powerManager,
+ dreamManager,
+ ambientDisplayConfiguration,
+ filter,
+ batteryController,
+ statusBarStateController,
+ headsUpManager,
+ mainHandler);
+ mUseHeadsUp = true;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
deleted file mode 100644
index 5d192b2..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.policy.BatteryController;
-
-public class TestableNotificationInterruptionStateProvider
- extends NotificationInterruptionStateProvider {
-
- TestableNotificationInterruptionStateProvider(Context context,
- NotificationFilter filter, StatusBarStateController controller,
- BatteryController batteryController) {
- super(context, filter, controller, batteryController);
- mUseHeadsUp = true;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index e3187cb9..b1ac022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -43,6 +43,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.IntSupplier;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -59,7 +60,13 @@
@Before
public void setUp() throws Exception {
super.setUp();
- mStackController = spy(new TestableStackController(mFloatingContentCoordinator));
+ mStackController = spy(new TestableStackController(
+ mFloatingContentCoordinator, new IntSupplier() {
+ @Override
+ public int getAsInt() {
+ return mLayout.getChildCount();
+ }
+ }));
mLayout.setActiveController(mStackController);
addOneMoreThanBubbleLimitBubbles();
mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
@@ -295,8 +302,9 @@
*/
private class TestableStackController extends StackAnimationController {
TestableStackController(
- FloatingContentCoordinator floatingContentCoordinator) {
- super(floatingContentCoordinator);
+ FloatingContentCoordinator floatingContentCoordinator,
+ IntSupplier bubbleCountSupplier) {
+ super(floatingContentCoordinator, bubbleCountSupplier);
}
@Override
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 8cb1af4..c25d4e2 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
@@ -52,8 +52,8 @@
class ControlsBindingControllerImplTest : SysuiTestCase() {
companion object {
- fun <T> any(): T = Mockito.any<T>()
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ fun <T> any(): T = Mockito.any<T>()
private val TEST_COMPONENT_NAME_1 = ComponentName("TEST_PKG", "TEST_CLS_1")
private val TEST_COMPONENT_NAME_2 = ComponentName("TEST_PKG", "TEST_CLS_2")
private val TEST_COMPONENT_NAME_3 = ComponentName("TEST_PKG", "TEST_CLS_3")
@@ -61,9 +61,16 @@
@Mock
private lateinit var mockControlsController: ControlsController
+
@Captor
private lateinit var subscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub>
+ @Captor
+ private lateinit var loadSubscriberCaptor: ArgumentCaptor<IControlsSubscriber.Stub>
+
+ @Captor
+ private lateinit var listStringCaptor: ArgumentCaptor<List<String>>
+
private val user = UserHandle.of(mContext.userId)
private val otherUser = UserHandle.of(user.identifier + 1)
@@ -114,8 +121,8 @@
val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
- verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor))
- subscriberCaptor.value.onSubscribe(Binder(), subscription)
+ verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor))
+ loadSubscriberCaptor.value.onSubscribe(Binder(), subscription)
canceller.run()
verify(subscription).cancel()
@@ -132,17 +139,36 @@
val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
- verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor))
+ verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor))
val b = Binder()
- subscriberCaptor.value.onSubscribe(b, subscription)
+ loadSubscriberCaptor.value.onSubscribe(b, subscription)
- subscriberCaptor.value.onComplete(b)
+ loadSubscriberCaptor.value.onComplete(b)
canceller.run()
verify(subscription, never()).cancel()
}
@Test
- fun testBindAndLoad_noCancelAfterOnError() {
+ fun testLoad_onCompleteRemovesTimeout() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ val subscription = mock(IControlsSubscription::class.java)
+
+ val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoad(capture(subscriberCaptor))
+ val b = Binder()
+ subscriberCaptor.value.onSubscribe(b, subscription)
+
+ subscriberCaptor.value.onComplete(b)
+ verify(providers[0]).cancelLoadTimeout()
+ }
+
+ @Test
+ fun testLoad_onErrorRemovesTimeout() {
val callback = object : ControlsBindingController.LoadCallback {
override fun error(message: String) {}
@@ -157,6 +183,25 @@
subscriberCaptor.value.onSubscribe(b, subscription)
subscriberCaptor.value.onError(b, "")
+ verify(providers[0]).cancelLoadTimeout()
+ }
+
+ @Test
+ fun testBindAndLoad_noCancelAfterOnError() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ val subscription = mock(IControlsSubscription::class.java)
+
+ val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoad(capture(loadSubscriberCaptor))
+ val b = Binder()
+ loadSubscriberCaptor.value.onSubscribe(b, subscription)
+
+ loadSubscriberCaptor.value.onError(b, "")
canceller.run()
verify(subscription, never()).cancel()
}
@@ -180,8 +225,13 @@
executor.runAllReady()
+ val subs = mock(IControlsSubscription::class.java)
verify(providers[0]).maybeBindAndSubscribe(
+ capture(listStringCaptor), capture(subscriberCaptor))
+ assertEquals(listStringCaptor.value,
listOf(controlInfo1.controlId, controlInfo2.controlId))
+
+ subscriberCaptor.value.onSubscribe(providers[0].token, subs)
}
@Test
@@ -191,7 +241,7 @@
executor.runAllReady()
- verify(providers[0], never()).unsubscribe()
+ verify(providers[0], never()).cancelSubscription(any())
}
@Test
@@ -202,12 +252,21 @@
StructureInfo(TEST_COMPONENT_NAME_1, "Home", listOf(controlInfo1, controlInfo2))
controller.subscribe(structure)
-
- controller.unsubscribe()
-
executor.runAllReady()
- verify(providers[0]).unsubscribe()
+ val subs = mock(IControlsSubscription::class.java)
+ verify(providers[0]).maybeBindAndSubscribe(
+ capture(listStringCaptor), capture(subscriberCaptor))
+ assertEquals(listStringCaptor.value,
+ listOf(controlInfo1.controlId, controlInfo2.controlId))
+
+ subscriberCaptor.value.onSubscribe(providers[0].token, subs)
+ executor.runAllReady()
+
+ controller.unsubscribe()
+ executor.runAllReady()
+
+ verify(providers[0]).cancelSubscription(subs)
}
@Test
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 fd92ad0..2d3757c 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
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (149the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
@@ -43,6 +43,7 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -83,7 +84,6 @@
context,
executor,
actionCallbackService,
- subscriberService,
UserHandle.of(0),
componentName
)
@@ -144,9 +144,22 @@
}
@Test
+ fun testMaybeBindAndLoad_timeoutCancelled() {
+ manager.maybeBindAndLoad(subscriberService)
+ executor.runAllReady()
+
+ manager.cancelLoadTimeout()
+
+ executor.advanceClockToLast()
+ executor.runAllReady()
+
+ verify(subscriberService, never()).onError(any(), anyString())
+ }
+
+ @Test
fun testMaybeBindAndSubscribe() {
val list = listOf("TEST_ID")
- manager.maybeBindAndSubscribe(list)
+ manager.maybeBindAndSubscribe(list, subscriberService)
executor.runAllReady()
assertTrue(mContext.isBound(componentName))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
new file mode 100644
index 0000000..ff5c8d4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.controller
+
+import android.content.ComponentName
+import android.os.Binder
+import android.service.controls.Control
+import android.service.controls.IControlsSubscription
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class StatefulControlSubscriberTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var controller: ControlsController
+
+ @Mock
+ private lateinit var subscription: IControlsSubscription
+
+ @Mock
+ private lateinit var provider: ControlsProviderLifecycleManager
+
+ @Mock
+ private lateinit var control: Control
+
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val token = Binder()
+ private val badToken = Binder()
+
+ private val TEST_COMPONENT = ComponentName("TEST_PKG", "TEST_CLS_1")
+
+ private lateinit var scs: StatefulControlSubscriber
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(provider.componentName).thenReturn(TEST_COMPONENT)
+ `when`(provider.token).thenReturn(token)
+ scs = StatefulControlSubscriber(controller, provider, executor)
+ }
+
+ @Test
+ fun testOnSubscribe() {
+ scs.onSubscribe(token, subscription)
+
+ executor.runAllReady()
+ verify(provider).startSubscription(subscription)
+ }
+
+ @Test
+ fun testOnSubscribe_badToken() {
+ scs.onSubscribe(badToken, subscription)
+
+ executor.runAllReady()
+ verify(provider, never()).startSubscription(subscription)
+ }
+
+ @Test
+ fun testOnNext() {
+ scs.onSubscribe(token, subscription)
+ scs.onNext(token, control)
+
+ executor.runAllReady()
+ verify(controller).refreshStatus(TEST_COMPONENT, control)
+ }
+
+ @Test
+ fun testOnNext_multiple() {
+ scs.onSubscribe(token, subscription)
+ scs.onNext(token, control)
+ scs.onNext(token, control)
+ scs.onNext(token, control)
+
+ executor.runAllReady()
+ verify(controller, times(3)).refreshStatus(TEST_COMPONENT, control)
+ }
+
+ @Test
+ fun testOnNext_noRefreshBeforeSubscribe() {
+ scs.onNext(token, control)
+
+ executor.runAllReady()
+ verify(controller, never()).refreshStatus(TEST_COMPONENT, control)
+ }
+
+ @Test
+ fun testOnNext_noRefreshAfterCancel() {
+ scs.onSubscribe(token, subscription)
+ executor.runAllReady()
+
+ scs.cancel()
+ scs.onNext(token, control)
+
+ executor.runAllReady()
+ verify(controller, never()).refreshStatus(TEST_COMPONENT, control)
+ }
+
+ @Test
+ fun testOnNext_noRefreshAfterError() {
+ scs.onSubscribe(token, subscription)
+ scs.onError(token, "Error")
+ scs.onNext(token, control)
+
+ executor.runAllReady()
+ verify(controller, never()).refreshStatus(TEST_COMPONENT, control)
+ }
+
+ @Test
+ fun testOnNext_noRefreshAfterComplete() {
+ scs.onSubscribe(token, subscription)
+ scs.onComplete(token)
+ scs.onNext(token, control)
+
+ executor.runAllReady()
+ verify(controller, never()).refreshStatus(TEST_COMPONENT, control)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 9117ea8..f535351 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -248,31 +248,6 @@
}
@Test
- public void pausingAod_softBlanks_withSpuriousSensorDuringPause() throws Exception {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
- mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
-
- reset(mDozeHost);
- mSensor.sendSensorEvent(1);
- verify(mDozeHost).setAodDimmingScrim(eq(1f));
- }
-
- @Test
- public void screenOff_softBlanks() throws Exception {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.transitionTo(DOZE_AOD, DOZE);
- verify(mDozeHost).setAodDimmingScrim(eq(1f));
-
- reset(mDozeHost);
- mScreen.transitionTo(DOZE, DOZE_AOD);
- mSensor.sendSensorEvent(2);
- verify(mDozeHost).setAodDimmingScrim(eq(0f));
- }
-
- @Test
public void pausingAod_unblanksAfterSensor() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 8320b05..6871aad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.DeviceConfigProxy;
@@ -71,6 +72,7 @@
private @Mock DumpManager mDumpManager;
private @Mock PowerManager mPowerManager;
private @Mock TrustManager mTrustManager;
+ private @Mock NavigationModeController mNavigationModeController;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -88,7 +90,7 @@
mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher,
mNotificationShadeWindowController, () -> mStatusBarKeyguardViewManager,
mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
- mPowerManager, mTrustManager, mDeviceConfig);
+ mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index a2ab784..b863f14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -58,7 +58,8 @@
@Before
public void setUp() throws Exception {
- mPipAnimationController = new PipAnimationController(mContext);
+ mPipAnimationController = new PipAnimationController(
+ mContext, new PipSurfaceTransactionHelper(mContext));
MockitoAnnotations.initMocks(this);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index b12db2b..0bf0f04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -24,6 +24,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
+import android.util.Size;
import android.view.DisplayInfo;
import android.view.Gravity;
@@ -51,6 +52,7 @@
private static final float MIN_ASPECT_RATIO = 0.5f;
private static final float MAX_ASPECT_RATIO = 2f;
private static final Rect EMPTY_CURRENT_BOUNDS = null;
+ private static final Size EMPTY_MINIMAL_SIZE = null;
private PipBoundsHandler mPipBoundsHandler;
private DisplayInfo mDefaultDisplayInfo;
@@ -119,7 +121,7 @@
};
for (float aspectRatio : aspectRatios) {
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, EMPTY_CURRENT_BOUNDS);
+ aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
assertEquals("Destination bounds matches the given aspect ratio",
@@ -135,7 +137,7 @@
};
for (float aspectRatio : invalidAspectRatios) {
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, EMPTY_CURRENT_BOUNDS);
+ aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
assertEquals("Destination bounds fallbacks to default aspect ratio",
@@ -151,7 +153,7 @@
currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, currentBounds);
+ aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -160,14 +162,58 @@
}
@Test
+ public void getDestinationBounds_withMinSize_returnMinBounds() {
+ final float[] aspectRatios = new float[] {
+ (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2,
+ DEFAULT_ASPECT_RATIO,
+ (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
+ };
+ final Size[] minimalSizes = new Size[] {
+ new Size((int) (100 * aspectRatios[0]), 100),
+ new Size((int) (100 * aspectRatios[1]), 100),
+ new Size((int) (100 * aspectRatios[2]), 100)
+ };
+ for (int i = 0; i < aspectRatios.length; i++) {
+ final float aspectRatio = aspectRatios[i];
+ final Size minimalSize = minimalSizes[i];
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
+ assertTrue("Destination bounds is no smaller than minimal requirement",
+ (destinationBounds.width() == minimalSize.getWidth()
+ && destinationBounds.height() >= minimalSize.getHeight())
+ || (destinationBounds.height() == minimalSize.getHeight()
+ && destinationBounds.width() >= minimalSize.getWidth()));
+ final float actualAspectRatio =
+ destinationBounds.width() / (destinationBounds.height() * 1f);
+ assertEquals("Destination bounds matches the given aspect ratio",
+ aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN);
+ }
+ }
+
+ @Test
+ public void getDestinationBounds_withCurrentBounds_ignoreMinBounds() {
+ final float aspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2;
+ final Rect currentBounds = new Rect(0, 0, 0, 100);
+ currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
+ final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
+
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ aspectRatio, currentBounds, minSize);
+
+ assertTrue("Destination bounds ignores minimal size",
+ destinationBounds.width() > minSize.getWidth()
+ && destinationBounds.height() > minSize.getHeight());
+ }
+
+ @Test
public void setShelfHeight_offsetBounds() {
final int shelfHeight = 100;
final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.setShelfHeight(true, shelfHeight);
final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -shelfHeight);
assertBoundsWithMargin("offsetBounds by shelf", oldPosition, newPosition);
@@ -177,11 +223,11 @@
public void onImeVisibilityChanged_offsetBounds() {
final int imeHeight = 100;
final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -imeHeight);
assertBoundsWithMargin("offsetBounds by IME", oldPosition, newPosition);
@@ -191,13 +237,13 @@
public void onSaveReentryBounds_restoreLastPosition() {
final ComponentName componentName = new ComponentName(mContext, "component1");
final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -100);
mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsWithMargin("restoreLastPosition", oldPosition, newPosition);
}
@@ -206,14 +252,14 @@
public void onResetReentryBounds_useDefaultBounds() {
final ComponentName componentName = new ComponentName(mContext, "component1");
final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final Rect newBounds = new Rect(defaultBounds);
newBounds.offset(0, -100);
mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
mPipBoundsHandler.onResetReentryBounds(componentName);
final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsWithMargin("useDefaultBounds", defaultBounds, actualBounds);
}
@@ -222,14 +268,14 @@
public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
final ComponentName componentName = new ComponentName(mContext, "component1");
final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final Rect newBounds = new Rect(defaultBounds);
newBounds.offset(0, -100);
mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsWithMargin("restoreLastPosition", newBounds, actualBounds);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
new file mode 100644
index 0000000..4d7e6ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.pip.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Size;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.FloatingContentCoordinator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipTouchHandler}, including but not limited to:
+ * - Update movement bounds based on new bounds
+ * - Update movement bounds based on IME/shelf
+ * - Update movement bounds to PipResizeHandler
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipTouchHandlerTest extends SysuiTestCase {
+ private static final int ROUNDING_ERROR_MARGIN = 10;
+ private static final float DEFAULT_ASPECT_RATIO = 1f;
+ private static final Rect EMPTY_CURRENT_BOUNDS = null;
+
+ private PipTouchHandler mPipTouchHandler;
+ private DisplayInfo mDefaultDisplayInfo;
+
+ @Mock
+ private IActivityManager mActivityManager;
+
+ @Mock
+ private IActivityTaskManager mIActivityTaskManager;
+
+ @Mock
+ private PipMenuActivityController mPipMenuActivityController;
+
+ @Mock
+ private InputConsumerController mInputConsumerController;
+
+ @Mock
+ private PipBoundsHandler mPipBoundsHandler;
+
+ @Mock
+ private PipTaskOrganizer mPipTaskOrganizer;
+
+ @Mock
+ private FloatingContentCoordinator mFloatingContentCoordinator;
+
+ @Mock
+ private DeviceConfigProxy mDeviceConfigProxy;
+
+
+ private PipSnapAlgorithm mPipSnapAlgorithm;
+ private PipMotionHelper mMotionHelper;
+ private PipResizeGestureHandler mPipResizeGestureHandler;
+
+ Rect mInsetBounds;
+ Rect mMinBounds;
+ Rect mCurBounds;
+ boolean mFromImeAdjustment;
+ boolean mFromShelfAdjustment;
+ int mDisplayRotation;
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
+ mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mIActivityTaskManager,
+ mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
+ mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy,
+ mPipSnapAlgorithm);
+ mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
+ mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
+ mPipTouchHandler.setPipMotionHelper(mMotionHelper);
+ mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
+
+ // Assume a display of 1000 x 1000
+ // inset of 10
+ mInsetBounds = new Rect(10, 10, 990, 990);
+ // minBounds of 100x100 bottom right corner
+ mMinBounds = new Rect(890, 890, 990, 990);
+ mCurBounds = new Rect();
+ mFromImeAdjustment = false;
+ mFromShelfAdjustment = false;
+ mDisplayRotation = 0;
+ }
+
+ @Test
+ public void updateMovementBounds_minBounds() {
+ Rect expectedMinMovementBounds = new Rect();
+ mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0);
+
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ assertEquals(expectedMinMovementBounds, mPipTouchHandler.mNormalMovementBounds);
+ verify(mPipResizeGestureHandler, times(1))
+ .updateMinSize(mMinBounds.width(), mMinBounds.height());
+ }
+
+ @Test
+ public void updateMovementBounds_maxBounds() {
+ Point displaySize = new Point();
+ mContext.getDisplay().getRealSize(displaySize);
+ Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1,
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
+ Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
+ Rect expectedMaxMovementBounds = new Rect();
+ mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0);
+
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ assertEquals(expectedMaxMovementBounds, mPipTouchHandler.mExpandedMovementBounds);
+ verify(mPipResizeGestureHandler, times(1))
+ .updateMaxSize(maxBounds.width(), maxBounds.height());
+ }
+
+ @Test
+ public void updateMovementBounds_withImeAdjustment_movesPip() {
+ mFromImeAdjustment = true;
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ verify(mMotionHelper, times(1)).animateToOffset(any(), anyInt());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 616399a..dbbbaac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -35,6 +35,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
@@ -84,7 +85,11 @@
@Mock
private NotificationMediaManager mNotificationMediaManager;
@Mock
+ private Executor mForegroundExecutor;
+ @Mock
private Executor mBackgroundExecutor;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
@Before
public void setup() throws Exception {
@@ -94,7 +99,8 @@
mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher,
- mQSLogger, mNotificationMediaManager, mBackgroundExecutor);
+ mQSLogger, mNotificationMediaManager, mForegroundExecutor, mBackgroundExecutor,
+ mLocalBluetoothManager);
// Provides a parent with non-zero size for QSPanel
mParentView = new FrameLayout(mContext);
mParentView.addView(mQsPanel);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 73f3ddd..95c3e5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -86,7 +86,7 @@
@Mock
private StatusBarIconController mIconController;
@Mock
- private QSFactoryImpl mDefaultFactory;
+ private QSFactory mDefaultFactory;
@Mock
private PluginManager mPluginManager;
@Mock
@@ -295,7 +295,7 @@
private static class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
- QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
+ QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 58be50e..953198c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -30,7 +30,7 @@
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Assert.assertEquals
@@ -56,7 +56,7 @@
val TILE_SPEC = CustomTile.toSpec(componentName)
}
- @Mock private lateinit var mTileHost: QSTileHost
+ @Mock private lateinit var mTileHost: QSHost
@Mock private lateinit var mTileService: IQSTileService
@Mock private lateinit var mTileServices: TileServices
@Mock private lateinit var mTileServiceManager: TileServiceManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 7c6da63..cffcabb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -409,11 +409,12 @@
public void testShowAuthenticationDialog() {
Bundle bundle = new Bundle();
String packageName = "test";
+ final long operationId = 1;
mCommandQueue.showAuthenticationDialog(bundle, null /* receiver */, 1, true, 3,
- packageName);
+ packageName, operationId);
waitForIdleSync();
verify(mCallbacks).showAuthenticationDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
- eq(packageName));
+ eq(packageName), eq(operationId));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
new file mode 100644
index 0000000..f061f34
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -0,0 +1,117 @@
+/*
+ * 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
+
+import android.app.WallpaperManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Choreographer
+import android.view.View
+import android.view.ViewRootImpl
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.*
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class NotificationShadeDepthControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var blurUtils: BlurUtils
+ @Mock private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var choreographer: Choreographer
+ @Mock private lateinit var wallpaperManager: WallpaperManager
+ @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var root: View
+ @Mock private lateinit var viewRootImpl: ViewRootImpl
+ @Mock private lateinit var shadeSpring: SpringAnimation
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var statusBarStateListener: StatusBarStateController.StateListener
+ private var statusBarState = StatusBarState.SHADE
+ private val maxBlur = 150
+ private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+
+ @Before
+ fun setup() {
+ `when`(root.viewRootImpl).thenReturn(viewRootImpl)
+ `when`(statusBarStateController.state).then { statusBarState }
+ `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
+ (answer.arguments[0] as Float * maxBlur).toInt()
+ }
+ notificationShadeDepthController = NotificationShadeDepthController(
+ statusBarStateController, blurUtils, biometricUnlockController,
+ keyguardStateController, choreographer, wallpaperManager,
+ notificationShadeWindowController, dumpManager)
+ notificationShadeDepthController.shadeSpring = shadeSpring
+ notificationShadeDepthController.root = root
+
+ val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
+ verify(statusBarStateController).addCallback(captor.capture())
+ statusBarStateListener = captor.value
+ }
+
+ @Test
+ fun setupListeners() {
+ verify(dumpManager).registerDumpable(anyString(), safeEq(notificationShadeDepthController))
+ }
+
+ @Test
+ fun onPanelExpansionChanged_apliesBlur_ifShade() {
+ notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
+ false /* tracking */)
+ verify(shadeSpring).animateToFinalPosition(eq(maxBlur.toFloat()))
+ }
+
+ @Test
+ fun onStateChanged_reevalutesBlurs_ifSameRadiusAndNewState() {
+ onPanelExpansionChanged_apliesBlur_ifShade()
+ clearInvocations(shadeSpring)
+
+ statusBarState = StatusBarState.KEYGUARD
+ statusBarStateListener.onStateChanged(statusBarState)
+ verify(shadeSpring).animateToFinalPosition(eq(0f))
+ }
+
+ @Test
+ fun updateGlobalDialogVisibility_schedulesUpdate() {
+ notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
+ verify(choreographer).postFrameCallback(any())
+ }
+
+ private fun <T : Any> safeEq(value: T): T {
+ return eq(value) ?: value
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index cc5f149..83877f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -52,6 +52,7 @@
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationListItem;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -285,9 +286,15 @@
public void notifyGroupChildAdded(ExpandableView row) {}
@Override
+ public void notifyGroupChildAdded(View v) {}
+
+ @Override
public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {}
@Override
+ public void notifyGroupChildRemoved(View v, ViewGroup childrenContainer) {}
+
+ @Override
public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {}
@Override
@@ -313,12 +320,22 @@
}
@Override
+ public void removeListItem(NotificationListItem li) {
+ removeContainerView(li.getView());
+ }
+
+ @Override
public void addContainerView(View v) {
mLayout.addView(v);
mRows.add(v);
}
@Override
+ public void addListItem(NotificationListItem li) {
+ addContainerView(li.getView());
+ }
+
+ @Override
public void setMaxDisplayedNotifications(int maxNotifications) {
if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
mViewHierarchyManager.onDynamicPrivacyChanged();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index fe8b89f..a58000d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -55,6 +55,7 @@
private boolean mIsVisuallyInterruptive = false;
private boolean mIsConversation = false;
private ShortcutInfo mShortcutInfo = null;
+ private boolean mIsBubble = false;
public RankingBuilder() {
}
@@ -82,6 +83,7 @@
mIsVisuallyInterruptive = ranking.visuallyInterruptive();
mIsConversation = ranking.isConversation();
mShortcutInfo = ranking.getShortcutInfo();
+ mIsBubble = ranking.isBubble();
}
public Ranking build() {
@@ -108,7 +110,8 @@
mCanBubble,
mIsVisuallyInterruptive,
mIsConversation,
- mShortcutInfo);
+ mShortcutInfo,
+ mIsBubble);
return ranking;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 312bb7f..972357e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -143,7 +143,7 @@
IMPORTANCE_DEFAULT,
null, null,
null, null, null, true, sentiment, false, -1, false, null, null, false, false,
- false, null);
+ false, null, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
@@ -162,7 +162,7 @@
null, null,
null, null, null, true,
Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, smartActions, null, false, false, false, null);
+ false, smartActions, null, false, false, false, null, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
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 b7184be..82de4a3 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
@@ -34,16 +34,17 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import android.annotation.Nullable;
@@ -83,13 +84,13 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -123,6 +124,8 @@
private NotifCollection mCollection;
private BatchableNotificationHandler mNotifHandler;
+ private InOrder mListenerInOrder;
+
private NoManSimulator mNoMan;
@Before
@@ -133,6 +136,8 @@
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(true);
+ mListenerInOrder = inOrder(mCollectionListener);
+
mCollection = new NotifCollection(
mStatusBarService,
mock(DumpManager.class),
@@ -159,10 +164,12 @@
.setRank(4747));
// THEN the listener is notified
- verify(mCollectionListener).onEntryInit(mEntryCaptor.capture());
- NotificationEntry entry = mEntryCaptor.getValue();
+ final NotificationEntry entry = mCollectionListener.getEntry(notif1.key);
- verify(mCollectionListener).onEntryAdded(entry);
+ mListenerInOrder.verify(mCollectionListener).onEntryInit(entry);
+ mListenerInOrder.verify(mCollectionListener).onEntryAdded(entry);
+ mListenerInOrder.verify(mCollectionListener).onRankingApplied();
+
assertEquals(notif1.key, entry.getKey());
assertEquals(notif1.sbn, entry.getSbn());
assertEquals(notif1.ranking, entry.getRanking());
@@ -215,12 +222,11 @@
assertEquals(entry2.getRanking(), capturedUpdate.getRanking());
// THEN onBuildList is called only once
- verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
- assertEquals(new ArraySet<>(Arrays.asList(
- capturedAdds.get(0),
- capturedAdds.get(1),
- capturedUpdate
- )), new ArraySet<>(mBuildListCaptor.getValue()));
+ verifyBuiltList(
+ List.of(
+ capturedAdds.get(0),
+ capturedAdds.get(1),
+ capturedUpdate));
}
@Test
@@ -234,9 +240,11 @@
.setRank(89));
// THEN the listener is notified
- verify(mCollectionListener).onEntryUpdated(mEntryCaptor.capture());
+ final NotificationEntry entry = mCollectionListener.getEntry(notif2.key);
- NotificationEntry entry = mEntryCaptor.getValue();
+ mListenerInOrder.verify(mCollectionListener).onEntryUpdated(entry);
+ mListenerInOrder.verify(mCollectionListener).onRankingApplied();
+
assertEquals(notif2.key, entry.getKey());
assertEquals(notif2.sbn, entry.getSbn());
assertEquals(notif2.ranking, entry.getRanking());
@@ -256,8 +264,10 @@
mNoMan.retractNotif(notif.sbn, REASON_APP_CANCEL);
// THEN the listener is notified
- verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL);
- verify(mCollectionListener).onEntryCleanUp(entry);
+ mListenerInOrder.verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL);
+ mListenerInOrder.verify(mCollectionListener).onEntryCleanUp(entry);
+ mListenerInOrder.verify(mCollectionListener).onRankingApplied();
+
assertEquals(notif.sbn, entry.getSbn());
assertEquals(notif.ranking, entry.getRanking());
}
@@ -415,8 +425,8 @@
// THEN the dismissed entry still appears in the notification set
assertEquals(
- new ArraySet<>(Collections.singletonList(entry1)),
- new ArraySet<>(mCollection.getActiveNotifs()));
+ new ArraySet<>(singletonList(entry1)),
+ new ArraySet<>(mCollection.getAllNotifs()));
}
@Test
@@ -444,7 +454,7 @@
mNoMan.retractNotif(notif2.sbn, REASON_CANCEL);
assertEquals(
new ArraySet<>(List.of(entry1, entry2, entry3)),
- new ArraySet<>(mCollection.getActiveNotifs()));
+ new ArraySet<>(mCollection.getAllNotifs()));
// WHEN the summary is dismissed by the user
mCollection.dismissNotification(entry1, defaultStats(entry1));
@@ -452,7 +462,7 @@
// THEN the summary is removed, but both children stick around
assertEquals(
new ArraySet<>(List.of(entry2, entry3)),
- new ArraySet<>(mCollection.getActiveNotifs()));
+ new ArraySet<>(mCollection.getAllNotifs()));
assertEquals(NOT_DISMISSED, entry2.getDismissState());
assertEquals(NOT_DISMISSED, entry3.getDismissState());
}
@@ -561,7 +571,7 @@
}
@Test
- public void testEndDismissInterceptionUpdatesDismissInterceptors() throws RemoteException {
+ public void testEndDismissInterceptionUpdatesDismissInterceptors() {
// GIVEN a collection with notifications with multiple dismiss interceptors
mInterceptor1.shouldInterceptDismissal = true;
mInterceptor2.shouldInterceptDismissal = true;
@@ -592,7 +602,7 @@
@Test(expected = IllegalStateException.class)
- public void testEndingDismissalOfNonInterceptedThrows() throws RemoteException {
+ public void testEndingDismissalOfNonInterceptedThrows() {
// GIVEN a collection with notifications with a dismiss interceptor that hasn't been called
mInterceptor1.shouldInterceptDismissal = false;
mCollection.addNotificationDismissInterceptor(mInterceptor1);
@@ -820,7 +830,7 @@
verify(mExtender3).shouldExtendLifetime(entry2, REASON_CLICK);
// THEN the entry is not removed
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
// THEN the entry properly records all extenders that returned true
assertEquals(Arrays.asList(mExtender1, mExtender2), entry2.mLifetimeExtenders);
@@ -841,7 +851,7 @@
// GIVEN a notification gets lifetime-extended by one of them
mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the last active extender expires (but new ones become active)
@@ -856,7 +866,7 @@
verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
// THEN the entry is not removed
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
// THEN the entry properly records all extenders that returned true
assertEquals(Arrays.asList(mExtender1, mExtender3), entry2.mLifetimeExtenders);
@@ -878,7 +888,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN one (but not all) of the extenders expires
@@ -886,7 +896,7 @@
mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
// THEN the entry is not removed
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
// THEN we don't re-query the extenders
verify(mExtender1, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
@@ -894,7 +904,7 @@
verify(mExtender3, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
// THEN the entry properly records all extenders that returned true
- assertEquals(Arrays.asList(mExtender1), entry2.mLifetimeExtenders);
+ assertEquals(singletonList(mExtender1), entry2.mLifetimeExtenders);
}
@Test
@@ -913,7 +923,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN all of the active extenders expire
@@ -923,7 +933,7 @@
mExtender1.callback.onEndLifetimeExtension(mExtender1, entry2);
// THEN the entry removed
- assertFalse(mCollection.getActiveNotifs().contains(entry2));
+ assertFalse(mCollection.getAllNotifs().contains(entry2));
verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN);
}
@@ -943,7 +953,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the notification is reposted
@@ -954,7 +964,7 @@
verify(mExtender2).cancelLifetimeExtension(entry2);
// THEN the notification is still present
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
}
@Test(expected = IllegalStateException.class)
@@ -973,7 +983,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN a lifetime extender makes a reentrant call during cancelLifetimeExtension()
@@ -1002,7 +1012,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the notification is reposted
@@ -1055,11 +1065,11 @@
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair(entry1, defaultStats(entry1)),
- new Pair(entry2, defaultStats(entry2))));
+ List.of(new Pair<>(entry1, defaultStats(entry1)),
+ new Pair<>(entry2, defaultStats(entry2))));
// THEN build list is only called one time
- verify(mBuildListener).onBuildList(any(Collection.class));
+ verifyBuiltList(List.of(entry1, entry2));
}
@Test
@@ -1074,8 +1084,8 @@
DismissedByUserStats stats1 = defaultStats(entry1);
DismissedByUserStats stats2 = defaultStats(entry2);
mCollection.dismissNotifications(
- List.of(new Pair(entry1, defaultStats(entry1)),
- new Pair(entry2, defaultStats(entry2))));
+ List.of(new Pair<>(entry1, defaultStats(entry1)),
+ new Pair<>(entry2, defaultStats(entry2))));
// THEN we send the dismissals to system server
verify(mStatusBarService).onNotificationClear(
@@ -1109,8 +1119,8 @@
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair(entry1, defaultStats(entry1)),
- new Pair(entry2, defaultStats(entry2))));
+ List.of(new Pair<>(entry1, defaultStats(entry1)),
+ new Pair<>(entry2, defaultStats(entry2))));
// THEN the entries are marked as dismissed
assertEquals(DISMISSED, entry1.getDismissState());
@@ -1134,8 +1144,8 @@
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair(entry1, defaultStats(entry1)),
- new Pair(entry2, defaultStats(entry2))));
+ List.of(new Pair<>(entry1, defaultStats(entry1)),
+ new Pair<>(entry2, defaultStats(entry2))));
// THEN all interceptors get checked
verify(mInterceptor1).shouldInterceptDismissal(entry1);
@@ -1162,7 +1172,7 @@
mCollection.dismissAllNotifications(entry1.getSbn().getUser().getIdentifier());
// THEN build list is only called one time
- verify(mBuildListener).onBuildList(any(Collection.class));
+ verifyBuiltList(List.of(entry1, entry2));
}
@Test
@@ -1271,13 +1281,18 @@
NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
}
- public CollectionEvent postNotif(NotificationEntryBuilder builder) {
+ private CollectionEvent postNotif(NotificationEntryBuilder builder) {
clearInvocations(mCollectionListener);
NotifEvent rawEvent = mNoMan.postNotif(builder);
verify(mCollectionListener).onEntryAdded(mEntryCaptor.capture());
return new CollectionEvent(rawEvent, requireNonNull(mEntryCaptor.getValue()));
}
+ private void verifyBuiltList(Collection<NotificationEntry> list) {
+ verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
+ assertEquals(new ArraySet<>(list), new ArraySet<>(mBuildListCaptor.getValue()));
+ }
+
private static class RecordingCollectionListener implements NotifCollectionListener {
private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
@@ -1303,6 +1318,14 @@
public void onEntryCleanUp(NotificationEntry entry) {
}
+ @Override
+ public void onRankingApplied() {
+ }
+
+ @Override
+ public void onRankingUpdate(RankingMap rankingMap) {
+ }
+
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/notification/collection/coordinator/ForegroundCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
index 67b1aad..407e1e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
@@ -223,7 +223,7 @@
.setPkg(TEST_PKG)
.setId(2)
.build();
- when(mNotifPipeline.getActiveNotifs()).thenReturn(List.of(entry1, entry2, entry2Other));
+ when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry2Other));
// GIVEN that entry2 is currently associated with a foreground service
when(mForegroundServiceController.getStandardLayoutKey(0, TEST_PKG))
@@ -253,7 +253,7 @@
.setPkg(TEST_PKG)
.setId(2)
.build();
- when(mNotifPipeline.getActiveNotifs()).thenReturn(List.of(entry));
+ when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry));
when(mForegroundServiceController.getStandardLayoutKey(0, TEST_PKG))
.thenReturn(entry.getKey());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
new file mode 100644
index 0000000..0c109c4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.collection.coordinator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class HeadsUpCoordinatorTest extends SysuiTestCase {
+
+ private HeadsUpCoordinator mCoordinator;
+
+ // captured listeners and pluggables:
+ private NotifCollectionListener mCollectionListener;
+ private NotifPromoter mNotifPromoter;
+ private NotifLifetimeExtender mNotifLifetimeExtender;
+ private OnHeadsUpChangedListener mOnHeadsUpChangedListener;
+ private NotifSection mNotifSection;
+
+ @Mock private NotifPipeline mNotifPipeline;
+ @Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private NotificationRemoteInputManager mRemoteInputManager;
+ @Mock private RemoteInputController mRemoteInputController;
+ @Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
+
+ private NotificationEntry mEntry;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+
+ mCoordinator = new HeadsUpCoordinator(
+ mHeadsUpManager,
+ mRemoteInputManager
+ );
+
+ mCoordinator.attach(mNotifPipeline);
+
+ // capture arguments:
+ ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
+ ArgumentCaptor.forClass(NotifCollectionListener.class);
+ ArgumentCaptor<NotifPromoter> notifPromoterCaptor =
+ ArgumentCaptor.forClass(NotifPromoter.class);
+ ArgumentCaptor<NotifLifetimeExtender> notifLifetimeExtenderCaptor =
+ ArgumentCaptor.forClass(NotifLifetimeExtender.class);
+ ArgumentCaptor<OnHeadsUpChangedListener> headsUpChangedListenerCaptor =
+ ArgumentCaptor.forClass(OnHeadsUpChangedListener.class);
+
+ verify(mNotifPipeline).addCollectionListener(notifCollectionCaptor.capture());
+ verify(mNotifPipeline).addPromoter(notifPromoterCaptor.capture());
+ verify(mNotifPipeline).addNotificationLifetimeExtender(
+ notifLifetimeExtenderCaptor.capture());
+ verify(mHeadsUpManager).addListener(headsUpChangedListenerCaptor.capture());
+
+ mCollectionListener = notifCollectionCaptor.getValue();
+ mNotifPromoter = notifPromoterCaptor.getValue();
+ mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
+ mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue();
+
+ mNotifSection = mCoordinator.getSection();
+ mNotifLifetimeExtender.setCallback(mEndLifetimeExtension);
+ mEntry = new NotificationEntryBuilder().build();
+ }
+
+ @Test
+ public void testPromotesCurrentHUN() {
+ // GIVEN the current HUN is set to mEntry
+ setCurrentHUN(mEntry);
+
+ // THEN only promote the current HUN, mEntry
+ assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry));
+ assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder().build()));
+ }
+
+ @Test
+ public void testIncludeInSectionCurrentHUN() {
+ // GIVEN the current HUN is set to mEntry
+ setCurrentHUN(mEntry);
+
+ // THEN only section the current HUN, mEntry
+ assertTrue(mNotifSection.isInSection(mEntry));
+ assertFalse(mNotifSection.isInSection(new NotificationEntryBuilder().build()));
+ }
+
+ @Test
+ public void testLifetimeExtendsCurrentHUN() {
+ // GIVEN there is a HUN, mEntry
+ setCurrentHUN(mEntry);
+
+ // THEN only the current HUN, mEntry, should be lifetimeExtended
+ assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, /* cancellationReason */ 0));
+ assertFalse(mNotifLifetimeExtender.shouldExtendLifetime(
+ new NotificationEntryBuilder().build(), /* cancellationReason */ 0));
+ }
+
+ @Test
+ public void testLifetimeExtensionEndsOnNewHUN() {
+ // GIVEN there was a HUN that was lifetime extended
+ setCurrentHUN(mEntry);
+ assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
+ mEntry, /* cancellation reason */ 0));
+
+ // WHEN there's a new HUN
+ NotificationEntry newHUN = new NotificationEntryBuilder().build();
+ setCurrentHUN(newHUN);
+
+ // THEN the old entry's lifetime extension should be cancelled
+ verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
+ }
+
+ @Test
+ public void testLifetimeExtensionEndsOnNoHUNs() {
+ // GIVEN there was a HUN that was lifetime extended
+ setCurrentHUN(mEntry);
+ assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
+ mEntry, /* cancellation reason */ 0));
+
+ // WHEN there's no longer a HUN
+ setCurrentHUN(null);
+
+ // THEN the old entry's lifetime extension should be cancelled
+ verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
+ }
+
+ @Test
+ public void testOnEntryRemovedRemovesHeadsUpNotification() {
+ // GIVEN the current HUN is mEntry
+ setCurrentHUN(mEntry);
+
+ // WHEN mEntry is removed from the notification collection
+ mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
+ when(mRemoteInputController.isSpinning(any())).thenReturn(false);
+
+ // THEN heads up manager should remove the entry
+ verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
+ }
+
+ private void setCurrentHUN(NotificationEntry entry) {
+ when(mHeadsUpManager.getTopEntry()).thenReturn(entry);
+ when(mHeadsUpManager.isAlerting(any())).thenReturn(false);
+ if (entry != null) {
+ when(mHeadsUpManager.isAlerting(entry.getKey())).thenReturn(true);
+ }
+ mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, entry != null);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 792b4d5..8143cf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -20,8 +20,10 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
@@ -33,12 +35,15 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewBarn;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +78,8 @@
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService;
@Mock private NotifInflaterImpl mNotifInflater;
+ @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Before
public void setUp() {
@@ -86,7 +93,10 @@
mock(PreparationCoordinatorLogger.class),
mNotifInflater,
mErrorManager,
- mService);
+ mock(NotifViewBarn.class),
+ mService,
+ mNotificationInterruptStateProvider,
+ mHeadsUpManager);
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
mCoordinator.attach(mNotifPipeline);
@@ -170,4 +180,24 @@
// THEN it isn't filtered from shade list
assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
}
+
+ @Test
+ public void testShowHUNOnInflationFinished() {
+ // WHEN a notification should HUN and its inflation is finished
+ when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true);
+ mCallback.onInflationFinished(mEntry);
+
+ // THEN we tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager).showNotification(mEntry);
+ }
+
+ @Test
+ public void testNoHUNOnInflationFinished() {
+ // WHEN a notification shouldn't HUN and its inflation is finished
+ when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false);
+ mCallback.onInflationFinished(mEntry);
+
+ // THEN we never tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager, never()).showNotification(mEntry);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
similarity index 63%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 1693e7f..f9c62e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.interruption;
import static android.app.Notification.FLAG_BUBBLE;
@@ -30,15 +30,14 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.service.dreams.IDreamManager;
@@ -50,7 +49,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -68,7 +66,7 @@
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
+public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Mock
PowerManager mPowerManager;
@@ -81,38 +79,36 @@
@Mock
StatusBarStateController mStatusBarStateController;
@Mock
- NotificationPresenter mPresenter;
- @Mock
HeadsUpManager mHeadsUpManager;
@Mock
- NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
- @Mock
BatteryController mBatteryController;
+ @Mock
+ Handler mMockHandler;
- private NotificationInterruptionStateProvider mNotifInterruptionStateProvider;
+ private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mNotifInterruptionStateProvider =
- new TestableNotificationInterruptionStateProvider(mContext,
+ new NotificationInterruptStateProviderImpl(
+ mContext.getContentResolver(),
mPowerManager,
mDreamManager,
mAmbientDisplayConfiguration,
mNotificationFilter,
+ mBatteryController,
mStatusBarStateController,
- mBatteryController);
+ mHeadsUpManager,
+ mMockHandler);
- mNotifInterruptionStateProvider.setUpWithPresenter(
- mPresenter,
- mHeadsUpManager,
- mHeadsUpSuppressor);
+ mNotifInterruptionStateProvider.mUseHeadsUp = true;
}
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#canAlertCommon(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#canAlertCommon(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills group suppression check.
*/
private void ensureStateForAlertCommon() {
@@ -121,17 +117,16 @@
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#canAlertAwakeCommon(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#canAlertAwakeCommon(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills launch fullscreen check.
*/
private void ensureStateForAlertAwakeCommon() {
- when(mPresenter.isDeviceInVrMode()).thenReturn(false);
when(mHeadsUpManager.isSnoozed(any())).thenReturn(false);
}
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills importance & DND checks.
*/
private void ensureStateForHeadsUpWhenAwake() throws RemoteException {
@@ -141,12 +136,11 @@
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
}
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills importance & DND checks.
*/
private void ensureStateForHeadsUpWhenDozing() {
@@ -158,7 +152,7 @@
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#shouldBubbleUp(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#shouldBubbleUp(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills importance & bubble checks.
*/
private void ensureStateForBubbleUp() {
@@ -166,75 +160,53 @@
ensureStateForAlertAwakeCommon();
}
- /**
- * Ensure that the disabled state is set correctly.
- */
@Test
- public void testDisableNotificationAlerts() {
- // Enabled by default
- assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse();
-
- // Disable alerts
- mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
- assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isTrue();
-
- // Enable alerts
- mNotifInterruptionStateProvider.setDisableNotificationAlerts(false);
- assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse();
- }
-
- /**
- * Ensure that the disabled alert state effects whether HUNs are enabled.
- */
- @Test
- public void testHunSettingsChange_enabled_butAlertsDisabled() {
- // Set up but without a mock change observer
- mNotifInterruptionStateProvider.setUpWithPresenter(
- mPresenter,
- mHeadsUpManager,
- mHeadsUpSuppressor);
-
- // HUNs enabled by default
- assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isTrue();
-
- // Set alerts disabled
- mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
-
- // No more HUNs
- assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse();
- }
-
- /**
- * Alerts can happen.
- */
- @Test
- public void testCanAlertCommon_true() {
- ensureStateForAlertCommon();
+ public void testDefaultSuppressorDoesNotSuppress() {
+ // GIVEN a suppressor without any overrides
+ final NotificationInterruptSuppressor defaultSuppressor =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return "defaultSuppressor";
+ }
+ };
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isTrue();
+
+ // THEN this suppressor doesn't suppress anything by default
+ assertThat(defaultSuppressor.suppressAwakeHeadsUp(entry)).isFalse();
+ assertThat(defaultSuppressor.suppressAwakeInterruptions(entry)).isFalse();
+ assertThat(defaultSuppressor.suppressInterruptions(entry)).isFalse();
}
- /**
- * Filtered out notifications don't alert.
- */
@Test
- public void testCanAlertCommon_false_filteredOut() {
- ensureStateForAlertCommon();
- when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
+ public void testShouldHeadsUpAwake() throws RemoteException {
+ ensureStateForHeadsUpWhenAwake();
- NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
}
- /**
- * Grouped notifications have different alerting behaviours, sometimes the alert for a
- * grouped notification may be suppressed {@link android.app.Notification#GROUP_ALERT_CHILDREN}.
- */
@Test
- public void testCanAlertCommon_false_suppressedForGroups() {
- ensureStateForAlertCommon();
+ public void testShouldNotHeadsUpAwake_flteredOut() throws RemoteException {
+ // GIVEN state for "heads up when awake" is true
+ ensureStateForHeadsUpWhenAwake();
+ // WHEN this entry should be filtered out
+ NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
+ when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true);
+
+ // THEN we shouldn't heads up this entry
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+ }
+
+ @Test
+ public void testShouldNotHeadsUp_suppressedForGroups() throws RemoteException {
+ // GIVEN state for "heads up when awake" is true
+ ensureStateForHeadsUpWhenAwake();
+
+ // WHEN the alert for a grouped notification is suppressed
+ // see {@link android.app.Notification#GROUP_ALERT_CHILDREN}
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
@@ -247,40 +219,40 @@
.setImportance(IMPORTANCE_DEFAULT)
.build();
- assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
+ // THEN this entry shouldn't HUN
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * HUNs while dozing can happen.
- */
@Test
- public void testShouldHeadsUpWhenDozing_true() {
+ public void testShouldHeadsUpWhenDozing() {
ensureStateForHeadsUpWhenDozing();
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
}
- /**
- * Ambient display can show HUNs for new notifications, this may be disabled.
- */
@Test
- public void testShouldHeadsUpWhenDozing_false_pulseDisabled() {
+ public void testShouldNotHeadsUpWhenDozing_pulseDisabled() {
+ // GIVEN state for "heads up when dozing" is true
ensureStateForHeadsUpWhenDozing();
+
+ // WHEN pulsing (HUNs when dozing) is disabled
when(mAmbientDisplayConfiguration.pulseOnNotificationEnabled(anyInt())).thenReturn(false);
+ // THEN this entry shouldn't HUN
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * If the device is not in ambient display or sleeping then we don't HUN.
- */
@Test
- public void testShouldHeadsUpWhenDozing_false_notDozing() {
+ public void testShouldNotHeadsUpWhenDozing_notDozing() {
+ // GIVEN state for "heads up when dozing" is true
ensureStateForHeadsUpWhenDozing();
+
+ // WHEN we're not dozing (in ambient display or sleeping)
when(mStatusBarStateController.isDozing()).thenReturn(false);
+ // THEN this entry shouldn't HUN
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
@@ -290,7 +262,7 @@
* {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}.
*/
@Test
- public void testShouldHeadsUpWhenDozing_false_suppressingAmbient() {
+ public void testShouldNotHeadsUpWhenDozing_suppressingAmbient() {
ensureStateForHeadsUpWhenDozing();
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
@@ -301,23 +273,18 @@
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't
- * get to pulse.
- */
@Test
- public void testShouldHeadsUpWhenDozing_false_lessImportant() {
+ public void testShouldNotHeadsUpWhenDozing_lessImportant() {
ensureStateForHeadsUpWhenDozing();
+ // Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't
+ // get to pulse
NotificationEntry entry = createNotification(IMPORTANCE_LOW);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * Heads up can happen.
- */
@Test
- public void testShouldHeadsUp_true() throws RemoteException {
+ public void testShouldHeadsUp() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -325,38 +292,11 @@
}
/**
- * Heads up notifications can be disabled in general.
- */
- @Test
- public void testShouldHeadsUp_false_noHunsAllowed() throws RemoteException {
- ensureStateForHeadsUpWhenAwake();
-
- // Set alerts disabled, this should cause heads up to be false
- mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
- assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse();
-
- NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
- assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
- }
-
- /**
- * If the device is dozing, we don't show as heads up.
- */
- @Test
- public void testShouldHeadsUp_false_dozing() throws RemoteException {
- ensureStateForHeadsUpWhenAwake();
- when(mStatusBarStateController.isDozing()).thenReturn(true);
-
- NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
- assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
- }
-
- /**
* If the notification is a bubble, and the user is not on AOD / lockscreen, then
* the bubble is shown rather than the heads up.
*/
@Test
- public void testShouldHeadsUp_false_bubble() throws RemoteException {
+ public void testShouldNotHeadsUp_bubble() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
// Bubble bit only applies to interruption when we're in the shade
@@ -369,7 +309,7 @@
* If we're not allowed to alert in general, we shouldn't be shown as heads up.
*/
@Test
- public void testShouldHeadsUp_false_alertCommonFalse() throws RemoteException {
+ public void testShouldNotHeadsUp_filtered() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
// Make canAlertCommon false by saying it's filtered out
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
@@ -383,7 +323,7 @@
* {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}.
*/
@Test
- public void testShouldHeadsUp_false_suppressPeek() throws RemoteException {
+ public void testShouldNotHeadsUp_suppressPeek() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -399,7 +339,7 @@
* to show as a heads up.
*/
@Test
- public void testShouldHeadsUp_false_lessImportant() throws RemoteException {
+ public void testShouldNotHeadsUp_lessImportant() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
@@ -410,7 +350,7 @@
* If the device is not in use then we shouldn't be shown as heads up.
*/
@Test
- public void testShouldHeadsUp_false_deviceNotInUse() throws RemoteException {
+ public void testShouldNotHeadsUp_deviceNotInUse() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -424,61 +364,58 @@
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * If something wants to suppress this heads up, then it shouldn't be shown as a heads up.
- */
@Test
- public void testShouldHeadsUp_false_suppressed() throws RemoteException {
+ public void testShouldNotHeadsUp_headsUpSuppressed() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(false);
+
+ // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up.
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeHeadsUp);
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
- verify(mHeadsUpSuppressor).canHeadsUp(any(), any());
}
- /**
- * On screen alerts don't happen when the device is in VR Mode.
- */
@Test
- public void testCanAlertAwakeCommon__false_vrMode() {
- ensureStateForAlertAwakeCommon();
- when(mPresenter.isDeviceInVrMode()).thenReturn(true);
+ public void testShouldNotHeadsUpAwake_awakeInterruptsSuppressed() throws RemoteException {
+ ensureStateForHeadsUpWhenAwake();
- NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+ // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up.
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeInterruptions);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
/**
* On screen alerts don't happen when the notification is snoozed.
*/
@Test
- public void testCanAlertAwakeCommon_false_snoozedPackage() {
- ensureStateForAlertAwakeCommon();
- when(mHeadsUpManager.isSnoozed(any())).thenReturn(true);
-
+ public void testShouldNotHeadsUp_snoozedPackage() {
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+ ensureStateForAlertAwakeCommon();
+
+ when(mHeadsUpManager.isSnoozed(entry.getSbn().getPackageName())).thenReturn(true);
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * On screen alerts don't happen when that package has just launched fullscreen.
- */
+
@Test
- public void testCanAlertAwakeCommon_false_justLaunchedFullscreen() {
+ public void testShouldNotHeadsUp_justLaunchedFullscreen() {
ensureStateForAlertAwakeCommon();
+ // On screen alerts don't happen when that package has just launched fullscreen.
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
entry.notifyFullScreenIntentLaunched();
- assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
/**
* Bubbles can happen.
*/
@Test
- public void testShouldBubbleUp_true() {
+ public void testShouldBubbleUp() {
ensureStateForBubbleUp();
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isTrue();
}
@@ -487,7 +424,7 @@
* If the notification doesn't have permission to bubble, it shouldn't bubble.
*/
@Test
- public void shouldBubbleUp_false_notAllowedToBubble() {
+ public void shouldNotBubbleUp_notAllowedToBubble() {
ensureStateForBubbleUp();
NotificationEntry entry = createBubble();
@@ -502,7 +439,7 @@
* If the notification isn't a bubble, it should definitely not show as a bubble.
*/
@Test
- public void shouldBubbleUp_false_notABubble() {
+ public void shouldNotBubbleUp_notABubble() {
ensureStateForBubbleUp();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -517,7 +454,7 @@
* If the notification doesn't have bubble metadata, it shouldn't bubble.
*/
@Test
- public void shouldBubbleUp_false_invalidMetadata() {
+ public void shouldNotBubbleUp_invalidMetadata() {
ensureStateForBubbleUp();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -529,24 +466,18 @@
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse();
}
- /**
- * If the notification can't heads up in general, it shouldn't bubble.
- */
@Test
- public void shouldBubbleUp_false_alertAwakeCommonFalse() {
+ public void shouldNotBubbleUp_suppressedInterruptions() {
ensureStateForBubbleUp();
- // Make alert common return false by pretending we're in VR mode
- when(mPresenter.isDeviceInVrMode()).thenReturn(true);
+ // If the notification can't heads up in general, it shouldn't bubble.
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse();
}
- /**
- * If the notification can't heads up in general, it shouldn't bubble.
- */
@Test
- public void shouldBubbleUp_false_alertCommonFalse() {
+ public void shouldNotBubbleUp_filteredOut() {
ensureStateForBubbleUp();
// Make canAlertCommon false by saying it's filtered out
@@ -592,20 +523,45 @@
.build();
}
- /**
- * Testable class overriding constructor.
- */
- public static class TestableNotificationInterruptionStateProvider extends
- NotificationInterruptionStateProvider {
-
- TestableNotificationInterruptionStateProvider(Context context,
- PowerManager powerManager, IDreamManager dreamManager,
- AmbientDisplayConfiguration ambientDisplayConfiguration,
- NotificationFilter notificationFilter,
- StatusBarStateController statusBarStateController,
- BatteryController batteryController) {
- super(context, powerManager, dreamManager, ambientDisplayConfiguration,
- notificationFilter, batteryController, statusBarStateController);
+ private final NotificationInterruptSuppressor
+ mSuppressAwakeHeadsUp =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return "suppressAwakeHeadsUp";
}
- }
+
+ @Override
+ public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+ return true;
+ }
+ };
+
+ private final NotificationInterruptSuppressor
+ mSuppressAwakeInterruptions =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return "suppressAwakeInterruptions";
+ }
+
+ @Override
+ public boolean suppressAwakeInterruptions(NotificationEntry entry) {
+ return true;
+ }
+ };
+
+ private final NotificationInterruptSuppressor
+ mSuppressInterruptions =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return "suppressInterruptions";
+ }
+
+ @Override
+ public boolean suppressInterruptions(NotificationEntry entry) {
+ return true;
+ }
+ };
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index e960185..c356e0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -329,10 +329,10 @@
@Test
public void testIconScrollXAfterTranslationAndReset() throws Exception {
mGroupRow.setTranslation(50);
- assertEquals(50, -mGroupRow.getEntry().expandedIcon.getScrollX());
+ assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
mGroupRow.resetTranslation();
- assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX());
+ assertEquals(0, mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 5d0349d..2e787ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.content.pm.LauncherApps;
import android.os.Handler;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -56,12 +57,14 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.icon.IconBuilder;
+import com.android.systemui.statusbar.notification.icon.IconManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -108,7 +111,7 @@
@Mock private NotificationEntryListener mEntryListener;
@Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
- @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ @Mock private NotificationInterruptStateProvider mNotificationInterruptionStateProvider;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationGutsManager mGutsManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@@ -245,14 +248,13 @@
mLockscreenUserManager,
pipeline,
stage,
- true, /* allowLongPress */
- mock(KeyguardBypassController.class),
- mock(StatusBarStateController.class),
- mGroupManager,
- mGutsManager,
mNotificationInterruptionStateProvider,
RowInflaterTask::new,
- mExpandableNotificationRowComponentBuilder);
+ mExpandableNotificationRowComponentBuilder,
+ new IconManager(
+ mEntryManager,
+ mock(LauncherApps.class),
+ new IconBuilder(mContext)));
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
@@ -284,7 +286,8 @@
false,
false,
false,
- null);
+ null,
+ false);
mRankingMap = new NotificationListenerService.RankingMap(new Ranking[] {ranking});
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 5a89fc4..dc3374b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -34,6 +34,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -53,6 +54,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.icon.IconBuilder;
+import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -93,6 +96,7 @@
private final NotifBindPipeline mBindPipeline;
private final NotifCollectionListener mBindPipelineEntryListener;
private final RowContentBindStage mBindStage;
+ private final IconManager mIconManager;
private StatusBarStateController mStatusBarStateController;
public NotificationTestHelper(Context context, TestableDependency dependency) {
@@ -106,6 +110,10 @@
mock(KeyguardBypassController.class), mock(NotificationGroupManager.class),
mock(ConfigurationControllerImpl.class));
mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ mIconManager = new IconManager(
+ mock(CommonNotifCollection.class),
+ mock(LauncherApps.class),
+ new IconBuilder(mContext));
NotificationContentInflater contentBinder = new NotificationContentInflater(
mock(NotifRemoteViewCache.class),
@@ -377,7 +385,7 @@
.build();
entry.setRow(row);
- entry.createIcons(mContext, entry.getSbn());
+ mIconManager.createIcons(entry);
row.setEntry(entry);
mBindPipelineEntryListener.onEntryInit(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
new file mode 100644
index 0000000..291c039
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.phone;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class KeyguardIndicationTextViewTest extends SysuiTestCase {
+
+ private KeyguardIndicationTextView mKeyguardIndicationTextView;
+
+ @Before
+ public void setup() {
+ mKeyguardIndicationTextView = new KeyguardIndicationTextView(mContext);
+ }
+
+ @Test
+ public void switchIndication_null_hideIndication() {
+ mKeyguardIndicationTextView.switchIndication(null /* text */);
+
+ assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.INVISIBLE);
+ assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("");
+ }
+
+ @Test
+ public void switchIndication_emptyText_hideIndication() {
+ mKeyguardIndicationTextView.switchIndication("" /* text */);
+
+ assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.INVISIBLE);
+ assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("");
+ }
+
+ @Test
+ public void switchIndication_newText_updateProperly() {
+ mKeyguardIndicationTextView.switchIndication("test_indication" /* text */);
+
+ assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("test_indication");
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
index 83e89bd..5320ecd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@
import android.app.IActivityManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -110,4 +112,13 @@
public void testSetForcePluginOpen_beforeStatusBarInitialization() {
mNotificationShadeWindowController.setForcePluginOpen(true);
}
+
+ @Test
+ public void setBackgroundBlurRadius_expandedWithBlurs() {
+ mNotificationShadeWindowController.setBackgroundBlurRadius(10);
+ verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
+
+ mNotificationShadeWindowController.setBackgroundBlurRadius(0);
+ verify(mNotificationShadeWindowView).setVisibility(eq(View.INVISIBLE));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index c5b6969..cc2d1c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -37,7 +37,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -81,7 +81,7 @@
@Mock private DockManager mDockManager;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
- @Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController;
+ @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
@Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
@Before
@@ -116,7 +116,7 @@
new CommandQueue(mContext),
mShadeController,
mDockManager,
- mNotificationShadeWindowBlurController,
+ mNotificationShadeDepthController,
mView,
mNotificationPanelViewController,
mStatusBarViewFactory);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 408dfc0..23f2637 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -50,6 +50,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -101,6 +102,8 @@
private SysuiColorExtractor mSysuiColorExtractor;
@Mock
private DockManager mDockManager;
+ @Mock
+ private BlurUtils mBlurUtils;
private static class AnimatorListener implements Animator.AnimatorListener {
@@ -215,7 +218,7 @@
mScrimController = new ScrimController(mLightBarController,
mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mSysuiColorExtractor,
- mDockManager);
+ mDockManager, mBlurUtils);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble);
mScrimController.setAnimatorListener(mAnimatorListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 1e4df27..b9c5b7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -67,10 +67,10 @@
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -183,7 +183,7 @@
mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class),
mock(NotificationLockscreenUserManager.class),
mKeyguardStateController,
- mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
+ mock(NotificationInterruptStateProvider.class), mock(MetricsLogger.class),
mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor,
mActivityIntentHelper, mBubbleController, mShadeController, mFeatureFlags,
mNotifPipeline, mNotifCollection)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index b9d2d22..318e9b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,8 +16,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -35,6 +36,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -48,12 +50,12 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -62,6 +64,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
@@ -72,6 +75,9 @@
private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
+ private NotificationInterruptStateProvider mNotificationInterruptStateProvider =
+ mock(NotificationInterruptStateProvider.class);
+ private NotificationInterruptSuppressor mInterruptSuppressor;
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private ShadeController mShadeController = mock(ShadeController.class);
@@ -95,11 +101,11 @@
mDependency.injectMockDependency(NotificationViewHierarchyManager.class);
mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
- mDependency.injectMockDependency(NotificationInterruptionStateProvider.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
mDependency.injectMockDependency(VisualStabilityManager.class);
mDependency.injectMockDependency(NotificationGutsManager.class);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
+ mDependency.injectMockDependency(ForegroundServiceNotificationListener.class);
NotificationEntryManager entryManager =
mDependency.injectMockDependency(NotificationEntryManager.class);
when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
@@ -107,18 +113,25 @@
NotificationShadeWindowView notificationShadeWindowView =
mock(NotificationShadeWindowView.class);
when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
+
mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
notificationShadeWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
- mock(NotificationAlertingManager.class), mock(KeyguardStateController.class),
+ mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class), mStatusBar,
- mock(ShadeControllerImpl.class), mCommandQueue, mInitController);
+ mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
+ mNotificationInterruptStateProvider);
+ mInitController.executePostInitTasks();
+ ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
+ ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
+ verify(mNotificationInterruptStateProvider).addSuppressor(suppressorCaptor.capture());
+ mInterruptSuppressor = suppressorCaptor.getValue();
}
@Test
- public void testHeadsUp_disabledStatusBar() {
+ public void testSuppressHeadsUp_disabledStatusBar() {
Notification n = new Notification.Builder(getContext(), "a").build();
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
@@ -130,12 +143,12 @@
false /* animate */);
TestableLooper.get(this).processAllMessages();
- assertFalse("The panel shouldn't allow heads up while disabled",
- mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
+ assertTrue("The panel should suppress heads up while disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
@Test
- public void testHeadsUp_disabledNotificationShade() {
+ public void testSuppressHeadsUp_disabledNotificationShade() {
Notification n = new Notification.Builder(getContext(), "a").build();
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
@@ -147,8 +160,39 @@
false /* animate */);
TestableLooper.get(this).processAllMessages();
- assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
- mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
+ assertTrue("The panel should suppress interruptions while notification shade "
+ + "disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ }
+
+ @Test
+ public void testSuppressInterruptions_vrMode() {
+ Notification n = new Notification.Builder(getContext(), "a").build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
+ mStatusBarNotificationPresenter.mVrMode = true;
+
+ assertTrue("Vr mode should suppress interruptions",
+ mInterruptSuppressor.suppressAwakeInterruptions(entry));
+ }
+
+ @Test
+ public void testSuppressInterruptions_statusBarAlertsDisabled() {
+ Notification n = new Notification.Builder(getContext(), "a").build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
+ when(mStatusBar.areNotificationAlertsDisabled()).thenReturn(true);
+
+ assertTrue("StatusBar alerts disabled shouldn't allow interruptions",
+ mInterruptSuppressor.suppressInterruptions(entry));
}
@Test
@@ -172,4 +216,3 @@
}
}
}
-
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e407927..679ac22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -42,7 +42,7 @@
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
-import android.content.Context;
+import android.content.ContentResolver;
import android.content.IntentFilter;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.fingerprint.FingerprintManager;
@@ -109,18 +109,18 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -130,6 +130,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
@@ -161,7 +162,7 @@
private StatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
- private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider;
@Mock private NotificationsController mNotificationsController;
@Mock private LightBarController mLightBarController;
@@ -179,7 +180,6 @@
@Mock private DozeScrimController mDozeScrimController;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock private BiometricUnlockController mBiometricUnlockController;
- @Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationListener mNotificationListener;
@Mock private KeyguardViewMediator mKeyguardViewMediator;
@@ -193,9 +193,9 @@
@Mock private NotificationEntryListener mEntryListener;
@Mock private NotificationFilter mNotificationFilter;
@Mock private NotificationAlertingManager mNotificationAlertingManager;
+ @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private AssistManager mAssistManager;
@@ -263,10 +263,12 @@
mPowerManager = new PowerManager(mContext, powerManagerService, thermalService,
Handler.createAsync(Looper.myLooper()));
- mNotificationInterruptionStateProvider =
- new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
+ mNotificationInterruptStateProvider =
+ new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+ mPowerManager,
mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
- mStatusBarStateController, mBatteryController);
+ mStatusBarStateController, mBatteryController, mHeadsUpManager,
+ new Handler(TestableLooper.get(this).getLooper()));
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -299,9 +301,6 @@
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
- mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
- mHeadsUpManager, mHeadsUpSuppressor);
-
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
@@ -348,7 +347,7 @@
),
mNotificationGutsManager,
notificationLogger,
- mNotificationInterruptionStateProvider,
+ mNotificationInterruptStateProvider,
mNotificationViewHierarchyManager,
mKeyguardViewMediator,
mNotificationAlertingManager,
@@ -562,7 +561,6 @@
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -578,7 +576,7 @@
.setImportance(IMPORTANCE_HIGH)
.build();
- assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -587,7 +585,6 @@
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -603,7 +600,7 @@
.setImportance(IMPORTANCE_HIGH)
.build();
- assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -612,7 +609,6 @@
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
@@ -625,7 +621,7 @@
.setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK)
.build();
- assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -634,7 +630,6 @@
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
@@ -646,7 +641,7 @@
.setImportance(IMPORTANCE_HIGH)
.build();
- assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -872,19 +867,21 @@
verify(mDozeServiceHost).setDozeSuppressed(false);
}
- public static class TestableNotificationInterruptionStateProvider extends
- NotificationInterruptionStateProvider {
+ public static class TestableNotificationInterruptStateProviderImpl extends
+ NotificationInterruptStateProviderImpl {
- TestableNotificationInterruptionStateProvider(
- Context context,
+ TestableNotificationInterruptStateProviderImpl(
+ ContentResolver contentResolver,
PowerManager powerManager,
IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter filter,
StatusBarStateController controller,
- BatteryController batteryController) {
- super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter,
- batteryController, controller);
+ BatteryController batteryController,
+ HeadsUpManager headsUpManager,
+ Handler mainHandler) {
+ super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter,
+ batteryController, controller, headsUpManager, mainHandler);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index a0d551c..cddbb9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -207,7 +207,7 @@
protected void setupNetworkController() {
// For now just pretend to be the data sim, so we can test that too.
mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
- when(mMockTm.isDataConnectionEnabled()).thenReturn(true);
+ when(mMockTm.isDataConnectionAllowed()).thenReturn(true);
setDefaultSubId(mSubId);
setSubscriptions(mSubId);
mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
@@ -235,7 +235,7 @@
subs.add(subscription);
}
when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs);
- when(mMockSm.getActiveAndHiddenSubscriptionInfoList()).thenReturn(subs);
+ when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(subs);
mNetworkController.doUpdateMobileControllers();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 3eb0c44..d8b6aac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -119,7 +119,7 @@
@Test
public void testNoInternetIcon_withDefaultSub() {
setupNetworkController();
- when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+ when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -133,7 +133,7 @@
@Test
public void testDataDisabledIcon_withDefaultSub() {
setupNetworkController();
- when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+ when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -147,7 +147,7 @@
@Test
public void testNonDefaultSIM_showsFullSignal_connected() {
setupNetworkController();
- when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+ when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
setupDefaultSignal();
setDefaultSubId(mSubId + 1);
updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
@@ -162,7 +162,7 @@
@Test
public void testNonDefaultSIM_showsFullSignal_disconnected() {
setupNetworkController();
- when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+ when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
setupDefaultSignal();
setDefaultSubId(mSubId + 1);
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
@@ -177,7 +177,7 @@
@Test
public void testDataDisabledIcon_UserNotSetup() {
setupNetworkController();
- when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+ when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -192,7 +192,7 @@
@Test
public void testAlwaysShowDataRatIcon() {
setupDefaultSignal();
- when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+ when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED,
TelephonyManager.NETWORK_TYPE_GSM);
diff --git a/packages/Tethering/common/TetheringLib/api/current.txt b/packages/Tethering/common/TetheringLib/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
new file mode 100644
index 0000000..e25d77d
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -0,0 +1,126 @@
+// Signature format: 2.0
+package android.net {
+
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
+ public final class TetheringConstants {
+ field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+ field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+ field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+ field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+ field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+ }
+
+ public class TetheringManager {
+ ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
+ method public int getLastTetherError(@NonNull String);
+ method @NonNull public String[] getTetherableBluetoothRegexs();
+ method @NonNull public String[] getTetherableIfaces();
+ method @NonNull public String[] getTetherableUsbRegexs();
+ method @NonNull public String[] getTetherableWifiRegexs();
+ method @NonNull public String[] getTetheredIfaces();
+ method @NonNull public String[] getTetheringErroredIfaces();
+ method public boolean isTetheringSupported();
+ method public boolean isTetheringSupported(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
+ method @Deprecated public int setUsbTethering(boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @Deprecated public int tether(@NonNull String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @Deprecated public int untether(@NonNull String);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_ETHERNET = 5; // 0x5
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
+ field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
+ field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public abstract static class TetheringManager.StartTetheringCallback {
+ ctor public TetheringManager.StartTetheringCallback();
+ method public void onTetheringFailed(int);
+ method public void onTetheringStarted();
+ }
+
+ public abstract static class TetheringManager.TetheringEventCallback {
+ ctor public TetheringManager.TetheringEventCallback();
+ method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+ method public void onError(@NonNull String, int);
+ method public void onOffloadStatusChanged(int);
+ method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheringSupported(boolean);
+ method public void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+ ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ }
+
+ public static class TetheringManager.TetheringRequest {
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+ }
+
+}
+
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt b/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/removed.txt b/packages/Tethering/common/TetheringLib/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/system-current.txt b/packages/Tethering/common/TetheringLib/api/system-current.txt
new file mode 100644
index 0000000..d6fcb62
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/system-current.txt
@@ -0,0 +1,104 @@
+// Signature format: 2.0
+package android.net {
+
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
+ public class TetheringManager {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_ETHERNET = 5; // 0x5
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
+ field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
+ field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public abstract static class TetheringManager.StartTetheringCallback {
+ ctor public TetheringManager.StartTetheringCallback();
+ method public void onTetheringFailed(int);
+ method public void onTetheringStarted();
+ }
+
+ public abstract static class TetheringManager.TetheringEventCallback {
+ ctor public TetheringManager.TetheringEventCallback();
+ method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+ method public void onError(@NonNull String, int);
+ method public void onOffloadStatusChanged(int);
+ method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheringSupported(boolean);
+ method public void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+ ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ }
+
+ public static class TetheringManager.TetheringRequest {
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+ }
+
+}
+
diff --git a/packages/Tethering/common/TetheringLib/api/system-removed.txt b/packages/Tethering/common/TetheringLib/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 8b8b9e5..48be0d9 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -64,16 +64,26 @@
dest.writeInt(mTetheringType);
}
+ /**
+ * Get the MAC address used to identify the client.
+ */
@NonNull
public MacAddress getMacAddress() {
return mMacAddress;
}
+ /**
+ * Get information on the list of addresses that are associated with the client.
+ */
@NonNull
public List<AddressInfo> getAddresses() {
return new ArrayList<>(mAddresses);
}
+ /**
+ * Get the type of tethering used by the client.
+ * @return one of the {@code TetheringManager#TETHERING_*} constants.
+ */
public int getTetheringType() {
return mTetheringType;
}
@@ -115,45 +125,47 @@
private final LinkAddress mAddress;
@Nullable
private final String mHostname;
- // TODO: use LinkAddress expiration time once it is supported
- private final long mExpirationTime;
/** @hide */
public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) {
- this(address, hostname, 0);
- }
-
- /** @hide */
- public AddressInfo(@NonNull LinkAddress address, String hostname, long expirationTime) {
this.mAddress = address;
this.mHostname = hostname;
- this.mExpirationTime = expirationTime;
}
private AddressInfo(Parcel in) {
- this(in.readParcelable(null), in.readString(), in.readLong());
+ this(in.readParcelable(null), in.readString());
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mAddress, flags);
dest.writeString(mHostname);
- dest.writeLong(mExpirationTime);
}
+ /**
+ * Get the link address (including prefix length and lifetime) used by the client.
+ *
+ * This may be an IPv4 or IPv6 address.
+ */
@NonNull
public LinkAddress getAddress() {
return mAddress;
}
+ /**
+ * Get the hostname that was advertised by the client when obtaining its address, if any.
+ */
@Nullable
public String getHostname() {
return mHostname;
}
- /** @hide TODO: use expiration time in LinkAddress */
+ /**
+ * Get the expiration time of the address assigned to the client.
+ * @hide
+ */
public long getExpirationTime() {
- return mExpirationTime;
+ return mAddress.getExpirationTime();
}
@Override
@@ -163,7 +175,7 @@
@Override
public int hashCode() {
- return Objects.hash(mAddress, mHostname, mExpirationTime);
+ return Objects.hash(mAddress, mHostname);
}
@Override
@@ -173,8 +185,7 @@
// Use .equals() for addresses as all changes, including address expiry changes,
// should be included.
return other.mAddress.equals(mAddress)
- && Objects.equals(mHostname, other.mHostname)
- && mExpirationTime == other.mExpirationTime;
+ && Objects.equals(mHostname, other.mHostname);
}
@NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index a18f5da..fd6f171 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -32,7 +32,7 @@
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
-public class TetheringConstants {
+public final class TetheringConstants {
/** An explicit private class to avoid exposing constructor.*/
private TetheringConstants() { }
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 4af5c53..04d6215 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -157,4 +157,49 @@
<!-- ComponentName of the service used to run no ui tether provisioning. -->
<string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
+
+ <!-- Enable tethering notification -->
+ <!-- Icons for showing tether enable notification.
+ Each item should have two elements and be separated with ";".
+
+ The first element is downstream types which is one of tethering. This element has to be
+ made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream
+ types and use "," to separate each combinations. Such as
+
+ USB|BT,WIFI|USB|BT
+
+ The second element is icon for the item. This element has to be composed by
+ <package name>:drawable/<resource name>. Such as
+
+ 1. com.android.networkstack.tethering:drawable/stat_sys_tether_general
+ 2. android:drawable/xxx
+
+ So the entire string of each item would be
+
+ USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general
+
+ NOTE: One config can be separated into two or more for readability. Such as
+
+ WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx
+
+ can be separated into
+
+ WIFI|USB;android:drawable/xxx
+ WIFI|BT;android:drawable/xxx
+ USB|BT;android:drawable/xxx
+ WIFI|USB|BT;android:drawable/xxx
+
+ Notification will not show if the downstream type isn't listed in array.
+ Empty array means disable notifications. -->
+ <!-- In AOSP, hotspot is configured to no notification by default. Because status bar has showed
+ an icon on the right side already -->
+ <string-array translatable="false" name="tethering_notification_icons">
+ <item>USB;com.android.networkstack.tethering:drawable/stat_sys_tether_usb</item>
+ <item>BT;com.android.networkstack.tethering:drawable/stat_sys_tether_bluetooth</item>
+ <item>WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general</item>
+ </string-array>
+ <!-- String for tether enable notification title. -->
+ <string name="tethering_notification_title">@string/tethered_notification_title</string>
+ <!-- String for tether enable notification message. -->
+ <string name="tethering_notification_message">@string/tethered_notification_message</string>
</resources>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index fe025c7..bbba3f3 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -16,6 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<overlayable name="TetheringConfig">
<policy type="product|system|vendor">
+ <!-- Params from config.xml that can be overlaid -->
<item type="array" name="config_tether_usb_regexs"/>
<item type="array" name="config_tether_ncm_regexs" />
<item type="array" name="config_tether_wifi_regexs"/>
@@ -31,6 +32,45 @@
<item type="string" name="config_mobile_hotspot_provision_response"/>
<item type="integer" name="config_mobile_hotspot_provision_check_period"/>
<item type="string" name="config_wifi_tether_enable"/>
+ <!-- Configuration values for TetheringNotificationUpdater -->
+ <!-- Icons for showing tether enable notification.
+ Each item should have two elements and be separated with ";".
+
+ The first element is downstream types which is one of tethering. This element has to be
+ made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream
+ types and use "," to separate each combinations. Such as
+
+ USB|BT,WIFI|USB|BT
+
+ The second element is icon for the item. This element has to be composed by
+ <package name>:drawable/<resource name>. Such as
+
+ 1. com.android.networkstack.tethering:drawable/stat_sys_tether_general
+ 2. android:drawable/xxx
+
+ So the entire string of each item would be
+
+ USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general
+
+ NOTE: One config can be separated into two or more for readability. Such as
+
+ WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx
+
+ can be separated into
+
+ WIFI|USB;android:drawable/xxx
+ WIFI|BT;android:drawable/xxx
+ USB|BT;android:drawable/xxx
+ WIFI|USB|BT;android:drawable/xxx
+
+ Notification will not show if the downstream type isn't listed in array.
+ Empty array means disable notifications. -->
+ <item type="array" name="tethering_notification_icons"/>
+ <!-- String for tether enable notification title. -->
+ <item type="string" name="tethering_notification_title"/>
+ <!-- String for tether enable notification message. -->
+ <item type="string" name="tethering_notification_message"/>
+ <!-- Params from config.xml that can be overlaid -->
</policy>
</overlayable>
</resources>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
index 792bce9..ba98a66 100644
--- a/packages/Tethering/res/values/strings.xml
+++ b/packages/Tethering/res/values/strings.xml
@@ -15,19 +15,21 @@
-->
<resources>
<!-- Shown when the device is tethered -->
- <!-- Strings for tethered notification title [CHAR LIMIT=200] -->
+ <!-- String for tethered notification title [CHAR LIMIT=200] -->
<string name="tethered_notification_title">Tethering or hotspot active</string>
- <!-- Strings for tethered notification message [CHAR LIMIT=200] -->
+ <!-- String for tethered notification message [CHAR LIMIT=200] -->
<string name="tethered_notification_message">Tap to set up.</string>
<!-- This notification is shown when tethering has been disabled on a user's device.
The device is managed by the user's employer. Tethering can't be turned on unless the
IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
- <!-- Strings for tether disabling notification title [CHAR LIMIT=200] -->
+ <!-- String for tether disabling notification title [CHAR LIMIT=200] -->
<string name="disable_tether_notification_title">Tethering is disabled</string>
- <!-- Strings for tether disabling notification message [CHAR LIMIT=200] -->
+ <!-- String for tether disabling notification message [CHAR LIMIT=200] -->
<string name="disable_tether_notification_message">Contact your admin for details</string>
- <!-- Strings for tether notification channel name [CHAR LIMIT=200] -->
+ <!-- This string should be consistent with the "Hotspot & tethering" text in the "Network and
+ Internet" settings page. That is currently the tether_settings_title_all string. -->
+ <!-- String for tether notification channel name [CHAR LIMIT=200] -->
<string name="notification_channel_tethering_status">Hotspot & tethering status</string>
</resources>
\ No newline at end of file
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 38f8609..6c0c432 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -24,6 +24,7 @@
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import android.net.INetd;
import android.net.INetworkStackStatusCallback;
@@ -448,7 +449,9 @@
final ArrayList<TetheredClient> leases = new ArrayList<>();
for (DhcpLeaseParcelable lease : leaseParcelables) {
final LinkAddress address = new LinkAddress(
- intToInet4AddressHTH(lease.netAddr), lease.prefixLength);
+ intToInet4AddressHTH(lease.netAddr), lease.prefixLength,
+ 0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */,
+ lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */);
final MacAddress macAddress;
try {
@@ -460,7 +463,7 @@
}
final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo(
- address, lease.hostname, lease.expTime);
+ address, lease.hostname);
leases.add(new TetheredClient(
macAddress,
Collections.singletonList(addressInfo),
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index a402ffa..15cdb6a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -39,8 +39,7 @@
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.SharedLog;
import android.os.Handler;
import android.provider.Settings;
@@ -89,8 +88,8 @@
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
- private final @NonNull OffloadTetheringStatsProvider mStatsProvider;
- private final @Nullable NetworkStatsProviderCallback mStatsProviderCb;
+ @Nullable
+ private final OffloadTetheringStatsProvider mStatsProvider;
private final SharedLog mLog;
private final HashMap<String, LinkProperties> mDownstreams;
private boolean mConfigInitialized;
@@ -124,19 +123,18 @@
mHandler = h;
mHwInterface = hwi;
mContentResolver = contentResolver;
- mStatsProvider = new OffloadTetheringStatsProvider();
mLog = log.forSubComponent(TAG);
mDownstreams = new HashMap<>();
mExemptPrefixes = new HashSet<>();
mLastLocalPrefixStrs = new HashSet<>();
- NetworkStatsProviderCallback providerCallback = null;
+ OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider();
try {
- providerCallback = nsm.registerNetworkStatsProvider(
- getClass().getSimpleName(), mStatsProvider);
+ nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
} catch (RuntimeException e) {
Log.wtf(TAG, "Cannot register offload stats provider: " + e);
+ provider = null;
}
- mStatsProviderCb = providerCallback;
+ mStatsProvider = provider;
}
/** Start hardware offload. */
@@ -185,7 +183,7 @@
// and we need to synchronize stats and limits between
// software and hardware forwarding.
updateStatsForAllUpstreams();
- mStatsProvider.pushTetherStats();
+ if (mStatsProvider != null) mStatsProvider.pushTetherStats();
}
@Override
@@ -198,7 +196,7 @@
// limits set take into account any software tethering
// traffic that has been happening in the meantime.
updateStatsForAllUpstreams();
- mStatsProvider.pushTetherStats();
+ if (mStatsProvider != null) mStatsProvider.pushTetherStats();
// [2] (Re)Push all state.
computeAndPushLocalPrefixes(UpdateType.FORCE);
pushAllDownstreamState();
@@ -217,10 +215,12 @@
// TODO: rev the HAL so that it provides an interface name.
updateStatsForCurrentUpstream();
- mStatsProvider.pushTetherStats();
- // Push stats to service does not cause the service react to it immediately.
- // Inform the service about limit reached.
- if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached();
+ if (mStatsProvider != null) {
+ mStatsProvider.pushTetherStats();
+ // Push stats to service does not cause the service react to it
+ // immediately. Inform the service about limit reached.
+ mStatsProvider.notifyLimitReached();
+ }
}
@Override
@@ -263,13 +263,17 @@
}
@VisibleForTesting
- class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider {
+ class OffloadTetheringStatsProvider extends NetworkStatsProvider {
// These stats must only ever be touched on the handler thread.
@NonNull
private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
@NonNull
private NetworkStats mUidStats = new NetworkStats(0L, 0);
+ /**
+ * A helper function that collect tether stats from local hashmap. Note that this does not
+ * invoke binder call.
+ */
@VisibleForTesting
@NonNull
NetworkStats getTetherStats(@NonNull StatsType how) {
@@ -280,14 +284,14 @@
final ForwardedStats value = kv.getValue();
final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
- stats = stats.addValues(entry);
+ stats = stats.addEntry(entry);
}
return stats;
}
@Override
- public void setLimit(String iface, long quotaBytes) {
+ public void onSetLimit(String iface, long quotaBytes) {
// Listen for all iface is necessary since upstream might be changed after limit
// is set.
mHandler.post(() -> {
@@ -315,13 +319,12 @@
*/
public void pushTetherStats() {
// TODO: remove the accumulated stats and report the diff from HAL directly.
- if (null == mStatsProviderCb) return;
final NetworkStats ifaceDiff =
getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
final NetworkStats uidDiff =
getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
try {
- mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
+ notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
mIfaceStats = mIfaceStats.add(ifaceDiff);
mUidStats = mUidStats.add(uidDiff);
} catch (RuntimeException e) {
@@ -330,7 +333,7 @@
}
@Override
- public void requestStatsUpdate(int token) {
+ public void onRequestStatsUpdate(int token) {
// Do not attempt to update stats by querying the offload HAL
// synchronously from a different thread than the Handler thread. http://b/64771555.
mHandler.post(() -> {
@@ -340,7 +343,7 @@
}
@Override
- public void setAlert(long quotaBytes) {
+ public void onSetAlert(long quotaBytes) {
// TODO: Ask offload HAL to notify alert without stopping traffic.
}
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index f89da84..3d8dbab 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -59,10 +59,8 @@
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
@@ -72,7 +70,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.EthernetManager;
@@ -128,7 +125,6 @@
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.networkstack.tethering.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -224,14 +220,13 @@
private final ActiveDataSubIdListener mActiveDataSubIdListener;
private final ConnectedClientsTracker mConnectedClientsTracker;
private final TetheringThreadExecutor mExecutor;
+ private final TetheringNotificationUpdater mNotificationUpdater;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
// All the usage of mTetheringEventCallback should run in the same thread.
private ITetheringEventCallback mTetheringEventCallback = null;
private volatile TetheringConfiguration mConfig;
private InterfaceSet mCurrentUpstreamIfaceSet;
- private Notification.Builder mTetheredNotificationBuilder;
- private int mLastNotificationId;
private boolean mRndisEnabled; // track the RNDIS function enabled state
// True iff. WiFi tethering should be started when soft AP is ready.
@@ -255,6 +250,7 @@
mContext = mDeps.getContext();
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
+ mNotificationUpdater = mDeps.getNotificationUpdater(mContext);
mPublicSync = new Object();
@@ -738,13 +734,10 @@
final ArrayList<String> erroredList = new ArrayList<>();
final ArrayList<Integer> lastErrorList = new ArrayList<>();
- boolean wifiTethered = false;
- boolean usbTethered = false;
- boolean bluetoothTethered = false;
-
final TetheringConfiguration cfg = mConfig;
mTetherStatesParcel = new TetherStatesParcel();
+ int downstreamTypesMask = DOWNSTREAM_NONE;
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
@@ -758,11 +751,11 @@
localOnlyList.add(iface);
} else if (tetherState.lastState == IpServer.STATE_TETHERED) {
if (cfg.isUsb(iface)) {
- usbTethered = true;
+ downstreamTypesMask |= (1 << TETHERING_USB);
} else if (cfg.isWifi(iface)) {
- wifiTethered = true;
+ downstreamTypesMask |= (1 << TETHERING_WIFI);
} else if (cfg.isBluetooth(iface)) {
- bluetoothTethered = true;
+ downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
}
tetherList.add(iface);
}
@@ -796,98 +789,7 @@
"error", TextUtils.join(",", erroredList)));
}
- if (usbTethered) {
- if (wifiTethered || bluetoothTethered) {
- showTetheredNotification(R.drawable.stat_sys_tether_general);
- } else {
- showTetheredNotification(R.drawable.stat_sys_tether_usb);
- }
- } else if (wifiTethered) {
- if (bluetoothTethered) {
- showTetheredNotification(R.drawable.stat_sys_tether_general);
- } else {
- /* We now have a status bar icon for WifiTethering, so drop the notification */
- clearTetheredNotification();
- }
- } else if (bluetoothTethered) {
- showTetheredNotification(R.drawable.stat_sys_tether_bluetooth);
- } else {
- clearTetheredNotification();
- }
- }
-
- private void showTetheredNotification(int id) {
- showTetheredNotification(id, true);
- }
-
- @VisibleForTesting
- protected void showTetheredNotification(int id, boolean tetheringOn) {
- NotificationManager notificationManager =
- (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
- .getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager == null) {
- return;
- }
- final NotificationChannel channel = new NotificationChannel(
- "TETHERING_STATUS",
- mContext.getResources().getString(R.string.notification_channel_tethering_status),
- NotificationManager.IMPORTANCE_LOW);
- notificationManager.createNotificationChannel(channel);
-
- if (mLastNotificationId != 0) {
- if (mLastNotificationId == id) {
- return;
- }
- notificationManager.cancel(null, mLastNotificationId);
- mLastNotificationId = 0;
- }
-
- Intent intent = new Intent();
- intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
-
- PendingIntent pi = PendingIntent.getActivity(
- mContext.createContextAsUser(UserHandle.CURRENT, 0), 0, intent, 0, null);
-
- Resources r = mContext.getResources();
- final CharSequence title;
- final CharSequence message;
-
- if (tetheringOn) {
- title = r.getText(R.string.tethered_notification_title);
- message = r.getText(R.string.tethered_notification_message);
- } else {
- title = r.getText(R.string.disable_tether_notification_title);
- message = r.getText(R.string.disable_tether_notification_message);
- }
-
- if (mTetheredNotificationBuilder == null) {
- mTetheredNotificationBuilder = new Notification.Builder(mContext, channel.getId());
- mTetheredNotificationBuilder.setWhen(0)
- .setOngoing(true)
- .setColor(mContext.getColor(
- android.R.color.system_notification_accent_color))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setCategory(Notification.CATEGORY_STATUS);
- }
- mTetheredNotificationBuilder.setSmallIcon(id)
- .setContentTitle(title)
- .setContentText(message)
- .setContentIntent(pi);
- mLastNotificationId = id;
-
- notificationManager.notify(null, mLastNotificationId, mTetheredNotificationBuilder.build());
- }
-
- @VisibleForTesting
- protected void clearTetheredNotification() {
- NotificationManager notificationManager =
- (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
- .getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager != null && mLastNotificationId != 0) {
- notificationManager.cancel(null, mLastNotificationId);
- mLastNotificationId = 0;
- }
+ mNotificationUpdater.onDownstreamChanged(downstreamTypesMask);
}
private class StateReceiver extends BroadcastReceiver {
@@ -1081,12 +983,10 @@
return;
}
- mWrapper.clearTetheredNotification();
+ // TODO: Add user restrictions notification.
final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
if (newlyDisallowed && isTetheringActiveOnDevice) {
- mWrapper.showTetheredNotification(
- R.drawable.stat_sys_tether_general, false);
mWrapper.untetherAll();
// TODO(b/148139325): send tetheringSupported on restriction change
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index e019c3a..0330dad 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -26,6 +26,8 @@
import android.os.IBinder;
import android.os.Looper;
+import androidx.annotation.NonNull;
+
import com.android.internal.util.StateMachine;
import java.util.ArrayList;
@@ -102,6 +104,13 @@
}
/**
+ * Get a reference to the TetheringNotificationUpdater to be used by tethering.
+ */
+ public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) {
+ return new TetheringNotificationUpdater(ctx);
+ }
+
+ /**
* Get tethering thread looper.
*/
public abstract Looper getTetheringLooper();
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
new file mode 100644
index 0000000..b97f752
--- /dev/null
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
@@ -0,0 +1,198 @@
+/*
+ * 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.connectivity.tethering;
+
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.ArrayRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.networkstack.tethering.R;
+
+/**
+ * A class to display tethering-related notifications.
+ *
+ * <p>This class is not thread safe, it is intended to be used only from the tethering handler
+ * thread. However the constructor is an exception, as it is called on another thread ;
+ * therefore for thread safety all members of this class MUST either be final or initialized
+ * to their default value (0, false or null).
+ *
+ * @hide
+ */
+public class TetheringNotificationUpdater {
+ private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
+ private static final String CHANNEL_ID = "TETHERING_STATUS";
+ private static final boolean NOTIFY_DONE = true;
+ private static final boolean NO_NOTIFY = false;
+ // Id to update and cancel tethering notification. Must be unique within the tethering app.
+ private static final int NOTIFY_ID = 20191115;
+ @VisibleForTesting
+ static final int NO_ICON_ID = 0;
+ @VisibleForTesting
+ static final int DOWNSTREAM_NONE = 0;
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ private final NotificationChannel mChannel;
+ // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
+ // This value has to be made 1 2 and 4, and OR'd with the others.
+ // WARNING : the constructor is called on a different thread. Thread safety therefore
+ // relies on this value being initialized to 0, and not any other value. If you need
+ // to change this, you will need to change the thread where the constructor is invoked,
+ // or to introduce synchronization.
+ private int mDownstreamTypesMask = DOWNSTREAM_NONE;
+
+ public TetheringNotificationUpdater(@NonNull final Context context) {
+ mContext = context;
+ mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ mChannel = new NotificationChannel(
+ CHANNEL_ID,
+ context.getResources().getString(R.string.notification_channel_tethering_status),
+ NotificationManager.IMPORTANCE_LOW);
+ mNotificationManager.createNotificationChannel(mChannel);
+ }
+
+ /** Called when downstream has changed */
+ public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
+ if (mDownstreamTypesMask == downstreamTypesMask) return;
+ mDownstreamTypesMask = downstreamTypesMask;
+ updateNotification();
+ }
+
+ private void updateNotification() {
+ final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
+
+ if (tetheringInactive || setupNotification() == NO_NOTIFY) {
+ clearNotification();
+ }
+ }
+
+ private void clearNotification() {
+ mNotificationManager.cancel(null /* tag */, NOTIFY_ID);
+ }
+
+ /**
+ * Returns the downstream types mask which convert from given string.
+ *
+ * @param types This string has to be made by "WIFI", "USB", "BT", and OR'd with the others.
+ *
+ * @return downstream types mask value.
+ */
+ @IntRange(from = 0, to = 7)
+ private int getDownstreamTypesMask(@NonNull final String types) {
+ int downstreamTypesMask = DOWNSTREAM_NONE;
+ final String[] downstreams = types.split("\\|");
+ for (String downstream : downstreams) {
+ if ("USB".equals(downstream.trim())) {
+ downstreamTypesMask |= (1 << TETHERING_USB);
+ } else if ("WIFI".equals(downstream.trim())) {
+ downstreamTypesMask |= (1 << TETHERING_WIFI);
+ } else if ("BT".equals(downstream.trim())) {
+ downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
+ }
+ }
+ return downstreamTypesMask;
+ }
+
+ /**
+ * Returns the icons {@link android.util.SparseArray} which get from given string-array resource
+ * id.
+ *
+ * @param id String-array resource id
+ *
+ * @return {@link android.util.SparseArray} with downstream types and icon id info.
+ */
+ @NonNull
+ private SparseArray<Integer> getIcons(@ArrayRes int id) {
+ final Resources res = mContext.getResources();
+ final String[] array = res.getStringArray(id);
+ final SparseArray<Integer> icons = new SparseArray<>();
+ for (String config : array) {
+ if (TextUtils.isEmpty(config)) continue;
+
+ final String[] elements = config.split(";");
+ if (elements.length != 2) {
+ Log.wtf(TAG,
+ "Unexpected format in Tethering notification configuration : " + config);
+ continue;
+ }
+
+ final String[] types = elements[0].split(",");
+ for (String type : types) {
+ int mask = getDownstreamTypesMask(type);
+ if (mask == DOWNSTREAM_NONE) continue;
+ icons.put(mask, res.getIdentifier(
+ elements[1].trim(), null /* defType */, null /* defPackage */));
+ }
+ }
+ return icons;
+ }
+
+ private boolean setupNotification() {
+ final Resources res = mContext.getResources();
+ final SparseArray<Integer> downstreamIcons = getIcons(R.array.tethering_notification_icons);
+
+ final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID);
+ if (iconId == NO_ICON_ID) return NO_NOTIFY;
+
+ final String title = res.getString(R.string.tethering_notification_title);
+ final String message = res.getString(R.string.tethering_notification_message);
+
+ showNotification(iconId, title, message);
+ return NOTIFY_DONE;
+ }
+
+ private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
+ @NonNull final String message) {
+ final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
+ final PendingIntent pi = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0),
+ 0 /* requestCode */, intent, 0 /* flags */, null /* options */);
+ final Notification notification =
+ new Notification.Builder(mContext, mChannel.getId())
+ .setSmallIcon(iconId)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setOngoing(true)
+ .setColor(mContext.getColor(
+ android.R.color.system_notification_accent_color))
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setCategory(Notification.CATEGORY_STATUS)
+ .setContentIntent(pi)
+ .build();
+
+ mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification);
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
index d85389a..a20a0df 100644
--- a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
+++ b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
@@ -20,6 +20,7 @@
import android.net.TetheredClient.AddressInfo
import android.net.TetheringManager.TETHERING_BLUETOOTH
import android.net.TetheringManager.TETHERING_USB
+import android.system.OsConstants.RT_SCOPE_UNIVERSE
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.testutils.assertParcelSane
@@ -30,11 +31,19 @@
private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67))
private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78))
-private val TEST_ADDR1 = LinkAddress(parseNumericAddress("192.168.113.3"), 24)
-private val TEST_ADDR2 = LinkAddress(parseNumericAddress("fe80::1:2:3"), 64)
+private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L)
+private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L)
private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname")
private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null)
+private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress(
+ parseNumericAddress(addr),
+ prefixLength,
+ 0 /* flags */,
+ RT_SCOPE_UNIVERSE,
+ expTime /* deprecationTime */,
+ expTime /* expirationTime */)
+
@RunWith(AndroidJUnit4::class)
@SmallTest
class TetheredClientTest {
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
index 56f3e21..1cdc3bb 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
@@ -46,23 +46,28 @@
private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A")
private val client1 = TetheredClient(client1Addr, listOf(
- AddressInfo(LinkAddress("192.168.43.44/32"), null /* hostname */, clock.time + 20)),
+ makeAddrInfo("192.168.43.44/32", null /* hostname */, clock.time + 20)),
TETHERING_WIFI)
private val wifiClient1 = makeWifiClient(client1Addr)
private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB")
- private val client2Exp30AddrInfo = AddressInfo(
- LinkAddress("192.168.43.45/32"), "my_hostname", clock.time + 30)
+ private val client2Exp30AddrInfo = makeAddrInfo(
+ "192.168.43.45/32", "my_hostname", clock.time + 30)
private val client2 = TetheredClient(client2Addr, listOf(
client2Exp30AddrInfo,
- AddressInfo(LinkAddress("2001:db8:12::34/72"), "other_hostname", clock.time + 10)),
+ makeAddrInfo("2001:db8:12::34/72", "other_hostname", clock.time + 10)),
TETHERING_WIFI)
private val wifiClient2 = makeWifiClient(client2Addr)
private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC")
private val client3 = TetheredClient(client3Addr,
- listOf(AddressInfo(LinkAddress("2001:db8:34::34/72"), "other_other_hostname",
- clock.time + 10)),
+ listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)),
TETHERING_USB)
+ private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) =
+ LinkAddress(addr).let {
+ AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope,
+ expTime /* deprecationTime */, expTime /* expirationTime */), hostname)
+ }
+
@Test
fun testUpdateConnectedClients() {
doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 7e62e5a..fe84086 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -33,6 +33,8 @@
import static com.android.testutils.MiscAssertsKt.assertThrows;
import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
+import static junit.framework.Assert.assertNotNull;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -61,8 +63,7 @@
import android.net.NetworkStats;
import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -108,12 +109,10 @@
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private NetworkStatsManager mStatsManager;
- @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb;
+ @Mock private INetworkStatsProviderCallback mTetherStatsProviderCb;
+ private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider;
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class);
- private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
- mTetherStatsProviderCaptor =
- ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
private MockContentResolver mContentResolver;
@@ -126,8 +125,6 @@
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
FakeSettingsProvider.clearSettingsProvider();
- when(mStatsManager.registerNetworkStatsProvider(anyString(), any()))
- .thenReturn(mTetherStatsProviderCb);
}
@After public void tearDown() throws Exception {
@@ -154,8 +151,14 @@
private OffloadController makeOffloadController() throws Exception {
OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
mHardware, mContentResolver, mStatsManager, new SharedLog("test"));
+ final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
+ tetherStatsProviderCaptor =
+ ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
verify(mStatsManager).registerNetworkStatsProvider(anyString(),
- mTetherStatsProviderCaptor.capture());
+ tetherStatsProviderCaptor.capture());
+ mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
+ assertNotNull(mTetherStatsProvider);
+ mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
return offload;
}
@@ -413,9 +416,6 @@
final OffloadController offload = makeOffloadController();
offload.start();
- final OffloadController.OffloadTetheringStatsProvider provider =
- mTetherStatsProviderCaptor.getValue();
-
final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0";
@@ -443,15 +443,15 @@
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
// Verify that the fetched stats are stored.
- final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE);
- final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID);
+ final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
+ final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
- .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
- .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+ .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
@@ -462,13 +462,12 @@
NetworkStats.class);
// Force pushing stats update to verify the stats reported.
- provider.pushTetherStats();
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
- ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+ mTetherStatsProvider.pushTetherStats();
+ verify(mTetherStatsProviderCb, times(1))
+ .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
-
when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
new ForwardedStats(100000, 100000));
offload.setUpstreamLinkProperties(null);
@@ -483,31 +482,31 @@
inOrder.verifyNoMoreInteractions();
// Verify that the stored stats is accumulated.
- final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE);
- final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID);
+ final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
+ final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
- .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
- .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+ .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
// Verify that only diff of stats is reported.
reset(mTetherStatsProviderCb);
- provider.pushTetherStats();
+ mTetherStatsProvider.pushTetherStats();
final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
- .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
- .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
- ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
+ .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
+ verify(mTetherStatsProviderCb, times(1))
+ .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
}
@@ -529,19 +528,18 @@
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue();
final InOrder inOrder = inOrder(mHardware);
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
// Applying an interface quota to the current upstream immediately sends it to the hardware.
- provider.setLimit(ethernetIface, ethernetLimit);
+ mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
waitForIdle();
inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
inOrder.verifyNoMoreInteractions();
// Applying an interface quota to another upstream does not take any immediate action.
- provider.setLimit(mobileIface, mobileLimit);
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
@@ -554,7 +552,7 @@
// Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
// to Long.MAX_VALUE.
- provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
waitForIdle();
inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
@@ -562,7 +560,7 @@
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- provider.setLimit(mobileIface, mobileLimit);
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
@@ -571,7 +569,7 @@
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
- provider.setLimit(mobileIface, mobileLimit);
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware).getForwardedStats(ethernetIface);
inOrder.verify(mHardware).stopOffloadControl();
@@ -587,7 +585,7 @@
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
}
@Test
@@ -691,7 +689,7 @@
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
// TODO: verify the exact stats reported.
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
verifyNoMoreInteractions(mTetherStatsProviderCb);
verifyNoMoreInteractions(mHardware);
}
@@ -756,7 +754,7 @@
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
verifyNoMoreInteractions(mTetherStatsProviderCb);
// TODO: verify local prefixes and downstreams are also pushed to the HAL.
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index af7ad66..820c852 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -46,6 +46,8 @@
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -53,7 +55,6 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
@@ -188,6 +189,7 @@
@Mock private NetworkRequest mNetworkRequest;
@Mock private ConnectivityManager mCm;
@Mock private EthernetManager mEm;
+ @Mock private TetheringNotificationUpdater mNotificationUpdater;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -207,6 +209,7 @@
private PhoneStateListener mPhoneStateListener;
private InterfaceConfigurationParcel mInterfaceConfiguration;
+
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
super(base);
@@ -249,11 +252,6 @@
if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE;
return super.getSystemServiceName(serviceClass);
}
-
- @Override
- public Context createContextAsUser(UserHandle user, int flags) {
- return mContext;
- }
}
public class MockIpServerDependencies extends IpServer.Dependencies {
@@ -315,12 +313,10 @@
public class MockTetheringDependencies extends TetheringDependencies {
StateMachine mUpstreamNetworkMonitorMasterSM;
ArrayList<IpServer> mIpv6CoordinatorNotifyList;
- int mIsTetheringSupportedCalls;
public void reset() {
mUpstreamNetworkMonitorMasterSM = null;
mIpv6CoordinatorNotifyList = null;
- mIsTetheringSupportedCalls = 0;
}
@Override
@@ -354,7 +350,6 @@
@Override
public boolean isTetheringSupported() {
- mIsTetheringSupportedCalls++;
return true;
}
@@ -384,6 +379,11 @@
// TODO: add test for bluetooth tethering.
return null;
}
+
+ @Override
+ public TetheringNotificationUpdater getNotificationUpdater(Context ctx) {
+ return mNotificationUpdater;
+ }
}
private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -472,7 +472,6 @@
when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats);
mServiceContext = new TestContext(mContext);
- when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(null);
mContentResolver = new MockContentResolver(mServiceContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
mIntents = new Vector<>();
@@ -605,7 +604,8 @@
// it creates a IpServer and sends out a broadcast indicating that the
// interface is "available".
if (emulateInterfaceStatusChanged) {
- assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There is 1 IpServer state change event: STATE_AVAILABLE
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
@@ -689,9 +689,8 @@
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
- // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
+ verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
// Emulate externally-visible WifiManager effects, when hotspot mode
// is being torn down.
@@ -917,7 +916,8 @@
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
- assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There is 1 IpServer state change event: STATE_AVAILABLE
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
@@ -961,9 +961,9 @@
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
- // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
- // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_TETHERED
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI));
/////
// We do not currently emulate any upstream being found.
@@ -1034,9 +1034,10 @@
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
- // There are 3 state change event:
- // AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
- assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There are 3 IpServer state change event:
+ // STATE_AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
+ verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI));
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
// This is called, but will throw.
verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
@@ -1071,9 +1072,6 @@
ural.onUserRestrictionsChanged();
verify(mockTethering, times(expectedInteractionsWithShowNotification))
- .showTetheredNotification(anyInt(), eq(false));
-
- verify(mockTethering, times(expectedInteractionsWithShowNotification))
.untetherAll();
}
@@ -1429,9 +1427,8 @@
verifyNoMoreInteractions(mNetd);
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
- // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
+ verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME));
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
index 6f692c8..b9c5f1d 100644
--- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
@@ -16,6 +16,9 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string translatable="false" name="config_mainBuiltInDisplayCutout"></string>
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation"></string>
+
<!-- Height of the status bar in portrait. The height should be
Max((status bar content height + waterfall top size), top cutout size) -->
<dimen name="status_bar_height_portrait">28dp</dimen>
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 5d699c0..5d97d21 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -1232,6 +1232,7 @@
*/
public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
@NonNull Region outRegion) {
+ windowId = resolveParentWindowIdLocked(windowId);
final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
if (observer != null) {
return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion);
@@ -1436,6 +1437,7 @@
*/
@Nullable
public WindowInfo findWindowInfoByIdLocked(int windowId) {
+ windowId = resolveParentWindowIdLocked(windowId);
final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
if (observer != null) {
return observer.findWindowInfoByIdLocked(windowId);
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 7fe086d..5de8171 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.Bundle;
+import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
@@ -38,11 +39,8 @@
import java.util.Collections;
import java.util.Optional;
-import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
/**
* Maintains an autofill inline suggestion session that communicates with the IME.
@@ -64,12 +62,12 @@
* side flow.
*
* <p>
- * This class is thread safe.
+ * This class should hold the same lock as {@link Session} as they call into each other.
*/
final class InlineSuggestionSession {
private static final String TAG = "AfInlineSuggestionSession";
- private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
+ private static final int INLINE_REQUEST_TIMEOUT_MS = 200;
@NonNull
private final InputMethodManagerInternal mInputMethodManagerInternal;
@@ -80,6 +78,8 @@
private final Object mLock;
@NonNull
private final ImeStatusListener mImeStatusListener;
+ @NonNull
+ private final Handler mHandler;
/**
* To avoid the race condition, one should not access {@code mPendingImeResponse} without
@@ -105,11 +105,12 @@
private boolean mImeInputViewStarted = false;
InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
- int userId, ComponentName componentName) {
+ int userId, ComponentName componentName, Handler handler, Object lock) {
mInputMethodManagerInternal = inputMethodManagerInternal;
mUserId = userId;
mComponentName = componentName;
- mLock = new Object();
+ mHandler = handler;
+ mLock = lock;
mImeStatusListener = new ImeStatusListener() {
@Override
public void onInputMethodStartInputView(AutofillId imeFieldId) {
@@ -137,7 +138,8 @@
};
}
- public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId) {
+ public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId,
+ @NonNull Consumer<InlineSuggestionsRequest> requestConsumer) {
if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequest called for " + autofillId);
synchronized (mLock) {
@@ -154,26 +156,16 @@
mUserId,
new InlineSuggestionsRequestInfo(mComponentName, autofillId, new Bundle()),
new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse,
- mImeStatusListener));
+ mImeStatusListener, requestConsumer, mHandler, mLock));
}
}
- public Optional<InlineSuggestionsRequest> waitAndGetInlineSuggestionsRequest() {
+ public Optional<InlineSuggestionsRequest> getInlineSuggestionsRequest() {
final CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
- if (pendingImeResponse == null) {
+ if (pendingImeResponse == null || !pendingImeResponse.isDone()) {
return Optional.empty();
}
- try {
- return Optional.ofNullable(pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS,
- TimeUnit.MILLISECONDS)).map(ImeResponse::getRequest);
- } catch (TimeoutException e) {
- Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
- } catch (CancellationException e) {
- Log.w(TAG, "Inline suggestions request cancelled");
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
- return Optional.empty();
+ return Optional.ofNullable(pendingImeResponse.getNow(null)).map(ImeResponse::getRequest);
}
public boolean hideInlineSuggestionsUi(@NonNull AutofillId autofillId) {
@@ -200,8 +192,7 @@
if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked without IMS request");
return false;
}
- // There is no need to wait on the CompletableFuture since it should have been completed
- // when {@link #waitAndGetInlineSuggestionsRequest()} was called.
+ // There is no need to wait on the CompletableFuture since it should have been completed.
ImeResponse imeResponse = completedImsResponse.getNow(null);
if (imeResponse == null) {
if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked with pending IMS response");
@@ -249,20 +240,48 @@
private static final class InlineSuggestionsRequestCallbackImpl
extends IInlineSuggestionsRequestCallback.Stub {
+ private final Object mLock;
+ @GuardedBy("mLock")
private final CompletableFuture<ImeResponse> mResponse;
+ @GuardedBy("mLock")
+ private final Consumer<InlineSuggestionsRequest> mRequestConsumer;
private final ImeStatusListener mImeStatusListener;
+ private final Handler mHandler;
+ private final Runnable mTimeoutCallback;
private InlineSuggestionsRequestCallbackImpl(CompletableFuture<ImeResponse> response,
- ImeStatusListener imeStatusListener) {
+ ImeStatusListener imeStatusListener,
+ Consumer<InlineSuggestionsRequest> requestConsumer,
+ Handler handler, Object lock) {
mResponse = response;
mImeStatusListener = imeStatusListener;
+ mRequestConsumer = requestConsumer;
+ mLock = lock;
+
+ mHandler = handler;
+ mTimeoutCallback = () -> {
+ Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
+ completeIfNot(null);
+ };
+ mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
+ }
+
+ private void completeIfNot(@Nullable ImeResponse response) {
+ synchronized (mLock) {
+ if (mResponse.isDone()) {
+ return;
+ }
+ mResponse.complete(response);
+ mRequestConsumer.accept(response == null ? null : response.mRequest);
+ mHandler.removeCallbacks(mTimeoutCallback);
+ }
}
@BinderThread
@Override
public void onInlineSuggestionsUnsupported() throws RemoteException {
if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
- mResponse.complete(null);
+ completeIfNot(null);
}
@BinderThread
@@ -281,9 +300,9 @@
mImeStatusListener.onInputMethodFinishInputView(imeFieldId);
}
if (request != null && callback != null) {
- mResponse.complete(new ImeResponse(request, callback));
+ completeIfNot(new ImeResponse(request, callback));
} else {
- mResponse.complete(null);
+ completeIfNot(null);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index e73f9ce..53afa6e 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -57,6 +57,7 @@
import com.android.internal.os.IResultReceiver;
import com.android.server.autofill.ui.InlineSuggestionFactory;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
@@ -255,8 +256,12 @@
mCallbacks.logAugmentedAutofillSelected(sessionId,
dataset.getId());
try {
- client.autofill(sessionId, dataset.getFieldIds(),
- dataset.getFieldValues());
+ final ArrayList<AutofillId> fieldIds = dataset.getFieldIds();
+ final int size = fieldIds.size();
+ final boolean hideHighlight = size == 1
+ && fieldIds.get(0).equals(focusedId);
+ client.autofill(sessionId, fieldIds, dataset.getFieldValues(),
+ hideHighlight);
} catch (RemoteException e) {
Slog.w(TAG, "Encounter exception autofilling the values");
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 538082d..de31118 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -18,6 +18,7 @@
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
@@ -113,7 +114,9 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* A session for a given activity.
@@ -306,7 +309,47 @@
/**
* Receiver of assist data from the app's {@link Activity}.
*/
- private final IAssistDataReceiver mAssistReceiver = new IAssistDataReceiver.Stub() {
+ private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
+
+ private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
+
+ @GuardedBy("mLock")
+ private InlineSuggestionsRequest mPendingInlineSuggestionsRequest;
+ @GuardedBy("mLock")
+ private FillRequest mPendingFillRequest;
+ @GuardedBy("mLock")
+ private CountDownLatch mCountDownLatch;
+
+ @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(
+ boolean isInlineRequest) {
+ mCountDownLatch = new CountDownLatch(isInlineRequest ? 2 : 1);
+ mPendingFillRequest = null;
+ mPendingInlineSuggestionsRequest = null;
+ return isInlineRequest ? (inlineSuggestionsRequest) -> {
+ synchronized (mLock) {
+ mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
+ mCountDownLatch.countDown();
+ maybeRequestFillLocked();
+ }
+ } : null;
+ }
+
+ void maybeRequestFillLocked() {
+ if (mCountDownLatch == null || mCountDownLatch.getCount() > 0
+ || mPendingFillRequest == null) {
+ return;
+ }
+ if (mPendingInlineSuggestionsRequest != null) {
+ mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+ mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
+ mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+ }
+ mRemoteFillService.onFillRequest(mPendingFillRequest);
+ mPendingInlineSuggestionsRequest = null;
+ mPendingFillRequest = null;
+ mCountDownLatch = null;
+ }
+
@Override
public void onHandleAssistData(Bundle resultData) throws RemoteException {
if (mRemoteFillService == null) {
@@ -401,17 +444,17 @@
final ArrayList<FillContext> contexts =
mergePreviousSessionLocked(/* forSave= */ false);
-
- final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
- mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
request = new FillRequest(requestId, contexts, mClientState, flags,
- inlineSuggestionsRequest.orElse(null));
+ /*inlineSuggestionsRequest=*/null);
+
+ mPendingFillRequest = request;
+ mCountDownLatch.countDown();
+ maybeRequestFillLocked();
}
if (mActivityToken != null) {
mService.sendActivityAssistDataToContentCapture(mActivityToken, resultData);
}
- mRemoteFillService.onFillRequest(request);
}
@Override
@@ -604,9 +647,15 @@
private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
int newState, int flags) {
if (isInlineSuggestionsEnabledLocked()) {
- mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId);
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
+ mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
+ if (inlineSuggestionsRequestConsumer != null) {
+ mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
+ inlineSuggestionsRequestConsumer);
+ }
+ } else {
+ mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
}
-
requestNewFillResponseLocked(viewState, newState, flags);
}
@@ -624,7 +673,7 @@
+ ", flags=" + flags + ")");
}
mForAugmentedAutofillOnly = true;
- triggerAugmentedAutofillLocked();
+ triggerAugmentedAutofillLocked(flags);
return;
}
@@ -707,7 +756,7 @@
setClientLocked(client);
mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
- componentName);
+ componentName, handler, mLock);
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
@@ -834,7 +883,7 @@
}
// Although "standard" autofill is disabled, it might still trigger augmented autofill
- if (triggerAugmentedAutofillLocked() != null) {
+ if (triggerAugmentedAutofillLocked(requestFlags) != null) {
mForAugmentedAutofillOnly = true;
if (sDebug) {
Slog.d(TAG, "Service disabled autofill for " + mComponentName
@@ -2465,7 +2514,7 @@
// triggered augmented autofill
if (!isSameViewEntered) {
if (sDebug) Slog.d(TAG, "trigger augmented autofill.");
- triggerAugmentedAutofillLocked();
+ triggerAugmentedAutofillLocked(flags);
} else {
if (sDebug) Slog.d(TAG, "skip augmented autofill for same view.");
}
@@ -2662,7 +2711,7 @@
private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
@Nullable String filterText) {
final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
- mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
+ mInlineSuggestionSession.getInlineSuggestionsRequest();
if (!inlineSuggestionsRequest.isPresent()) {
Log.w(TAG, "InlineSuggestionsRequest unavailable");
return false;
@@ -2863,8 +2912,8 @@
// The default autofill service cannot fullfill the request, let's check if the augmented
// autofill service can.
- mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked();
- if (mAugmentedAutofillDestroyer == null) {
+ mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags);
+ if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) {
if (sVerbose) {
Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot "
+ "be augmented. AutofillableIds: " + autofillableIds);
@@ -2874,8 +2923,14 @@
removeSelf();
} else {
if (sVerbose) {
- Slog.v(TAG, "keeping session " + id + " when service returned null but "
- + "it can be augmented. AutofillableIds: " + autofillableIds);
+ if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
+ Slog.v(TAG, "keeping session " + id + " when service returned null and "
+ + "augmented service is disabled for password fields. "
+ + "AutofillableIds: " + autofillableIds);
+ } else {
+ Slog.v(TAG, "keeping session " + id + " when service returned null but "
+ + "it can be augmented. AutofillableIds: " + autofillableIds);
+ }
}
mAugmentedAutofillableIds = autofillableIds;
try {
@@ -2889,12 +2944,20 @@
/**
* Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
*
+ * <p> The request may not have been sent when this method returns as it may be waiting for
+ * the inline suggestion request asynchronously.
+ *
* @return callback to destroy the autofill UI, or {@code null} if not supported.
*/
// TODO(b/123099468): might need to call it in other places, like when the service returns a
// non-null response but without datasets (for example, just SaveInfo)
@GuardedBy("mLock")
- private Runnable triggerAugmentedAutofillLocked() {
+ private Runnable triggerAugmentedAutofillLocked(int flags) {
+ // (TODO: b/141703197) Fix later by passing info to service.
+ if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
+ return null;
+ }
+
// Check if Smart Suggestions is supported...
final @SmartSuggestionMode int supportedModes = mService
.getSupportedSmartSuggestionModesLocked();
@@ -2966,6 +3029,21 @@
final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
+ final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill =
+ (inlineSuggestionsRequest) -> {
+ remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName,
+ focusedId,
+ currentValue, inlineSuggestionsRequest,
+ /*inlineSuggestionsCallback=*/
+ response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
+ mCurrentViewId, response),
+ /*onErrorCallback=*/ () -> {
+ synchronized (mLock) {
+ cancelAugmentedAutofillLocked();
+ }
+ }, mService.getRemoteInlineSuggestionRenderServiceLocked());
+ };
+
// There are 3 cases when augmented autofill should ask IME for a new request:
// 1. standard autofill provider is None
// 2. standard autofill provider doesn't support inline (and returns null response)
@@ -2973,21 +3051,12 @@
// doesn't want autofill
if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabledLocked()) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
- mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId);
+ mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
+ /*requestConsumer=*/ requestAugmentedAutofill);
+ } else {
+ requestAugmentedAutofill.accept(
+ mInlineSuggestionSession.getInlineSuggestionsRequest().orElse(null));
}
-
- Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
- mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
- remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
- currentValue, inlineSuggestionsRequest.orElse(null),
- response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
- mCurrentViewId, response),
- () -> {
- synchronized (mLock) {
- cancelAugmentedAutofillLocked();
- }
- }, mService.getRemoteInlineSuggestionRenderServiceLocked());
-
if (mAugmentedAutofillDestroyer == null) {
mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
}
@@ -3370,6 +3439,8 @@
final List<AutofillId> ids = new ArrayList<>(entryCount);
final List<AutofillValue> values = new ArrayList<>(entryCount);
boolean waitingDatasetAuth = false;
+ boolean hideHighlight = (entryCount == 1
+ && dataset.getFieldIds().get(0).equals(mCurrentViewId));
for (int i = 0; i < entryCount; i++) {
if (dataset.getFieldValues().get(i) == null) {
continue;
@@ -3393,7 +3464,7 @@
}
if (sDebug) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
- mClient.autofill(id, ids, values);
+ mClient.autofill(id, ids, values, hideHighlight);
if (dataset.getId() != null) {
if (mSelectedDatasetIds == null) {
mSelectedDatasetIds = new ArrayList<>();
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index ee59d89..0ca9dd9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -329,8 +329,14 @@
@NonNull Runnable onErrorCallback) {
return new IInlineSuggestionUiCallback.Stub() {
@Override
- public void onAutofill() throws RemoteException {
+ public void onClick() throws RemoteException {
onAutofillCallback.run();
+ callback.onClick();
+ }
+
+ @Override
+ public void onLongClick() throws RemoteException {
+ callback.onLongClick();
}
@Override
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 6247a63..69154b4 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -157,7 +157,6 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Random;
@@ -174,29 +173,47 @@
public static class BackupWakeLock {
private final PowerManager.WakeLock mPowerManagerWakeLock;
private boolean mHasQuit = false;
+ private int mUserId;
- public BackupWakeLock(PowerManager.WakeLock powerManagerWakeLock) {
+ public BackupWakeLock(PowerManager.WakeLock powerManagerWakeLock, int userId) {
mPowerManagerWakeLock = powerManagerWakeLock;
+ mUserId = userId;
}
/** Acquires the {@link PowerManager.WakeLock} if hasn't been quit. */
public synchronized void acquire() {
if (mHasQuit) {
- Slog.v(TAG, "Ignore wakelock acquire after quit: " + mPowerManagerWakeLock.getTag());
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Ignore wakelock acquire after quit: "
+ + mPowerManagerWakeLock.getTag()));
return;
}
mPowerManagerWakeLock.acquire();
- Slog.v(TAG, "Acquired wakelock:" + mPowerManagerWakeLock.getTag());
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Acquired wakelock:" + mPowerManagerWakeLock.getTag()));
}
/** Releases the {@link PowerManager.WakeLock} if hasn't been quit. */
public synchronized void release() {
if (mHasQuit) {
- Slog.v(TAG, "Ignore wakelock release after quit: " + mPowerManagerWakeLock.getTag());
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Ignore wakelock release after quit: "
+ + mPowerManagerWakeLock.getTag()));
return;
}
mPowerManagerWakeLock.release();
- Slog.v(TAG, "Released wakelock:" + mPowerManagerWakeLock.getTag());
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Released wakelock:" + mPowerManagerWakeLock.getTag()));
}
/**
@@ -209,7 +226,10 @@
/** Release the {@link PowerManager.WakeLock} till it isn't held. */
public synchronized void quit() {
while (mPowerManagerWakeLock.isHeld()) {
- Slog.v(TAG, "Releasing wakelock: " + mPowerManagerWakeLock.getTag());
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Releasing wakelock: " + mPowerManagerWakeLock.getTag()));
mPowerManagerWakeLock.release();
}
mHasQuit = true;
@@ -439,7 +459,9 @@
}
if (DEBUG) {
- Slog.v(TAG, "Starting with transport " + currentTransport);
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(userId, "Starting with transport " + currentTransport));
}
TransportManager transportManager =
new TransportManager(userId, context, transportWhitelist, currentTransport);
@@ -451,7 +473,9 @@
new HandlerThread("backup-" + userId, Process.THREAD_PRIORITY_BACKGROUND);
userBackupThread.start();
if (DEBUG) {
- Slog.d(TAG, "Started thread " + userBackupThread.getName() + " for user " + userId);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(userId, "Started thread " + userBackupThread.getName()));
}
return createAndInitializeService(
@@ -556,7 +580,10 @@
if (userId == UserHandle.USER_SYSTEM) {
mBaseStateDir.mkdirs();
if (!SELinux.restorecon(mBaseStateDir)) {
- Slog.w(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ userId, "SELinux restorecon failed on " + mBaseStateDir));
}
}
@@ -604,7 +631,8 @@
addPackageParticipantsLocked(null);
}
- mTransportManager = Objects.requireNonNull(transportManager, "transportManager cannot be null");
+ mTransportManager =
+ Objects.requireNonNull(transportManager, "transportManager cannot be null");
mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
mBackupHandler.postDelayed(
@@ -620,7 +648,7 @@
mWakelock = new BackupWakeLock(
mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
- "*backup*-" + userId + "-" + userBackupThread.getThreadId()));
+ "*backup*-" + userId + "-" + userBackupThread.getThreadId()), userId);
// Set up the various sorts of package tracking we do
mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
@@ -869,7 +897,7 @@
}
private void initPackageTracking() {
- if (MORE_DEBUG) Slog.v(TAG, "` tracking");
+ if (MORE_DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "` tracking"));
// Remember our ancestral dataset
mTokenFile = new File(mBaseStateDir, "ancestral");
@@ -891,9 +919,9 @@
}
} catch (FileNotFoundException fnf) {
// Probably innocuous
- Slog.v(TAG, "No ancestral data");
+ Slog.v(TAG, addUserIdToLogMessage(mUserId, "No ancestral data"));
} catch (IOException e) {
- Slog.w(TAG, "Unable to read token file", e);
+ Slog.w(TAG, addUserIdToLogMessage(mUserId, "Unable to read token file"), e);
}
mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
@@ -941,7 +969,10 @@
DataInputStream in = new DataInputStream(bufStream)) {
int version = in.readInt();
if (version != SCHEDULE_FILE_VERSION) {
- Slog.e(TAG, "Unknown backup schedule version " + version);
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Unknown backup schedule version " + version));
return null;
}
@@ -966,14 +997,14 @@
schedule.add(new FullBackupEntry(pkgName, lastBackup));
} else {
if (DEBUG) {
- Slog.i(TAG, "Package " + pkgName
- + " no longer eligible for full backup");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Package " + pkgName
+ + " no longer eligible for full backup"));
}
}
} catch (NameNotFoundException e) {
if (DEBUG) {
- Slog.i(TAG, "Package " + pkgName
- + " not installed; dropping from full backup");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Package " + pkgName
+ + " not installed; dropping from full backup"));
}
}
}
@@ -986,7 +1017,13 @@
mUserId)) {
if (!foundApps.contains(app.packageName)) {
if (MORE_DEBUG) {
- Slog.i(TAG, "New full backup app " + app.packageName + " found");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "New full backup app "
+ + app.packageName
+ + " found"));
}
schedule.add(new FullBackupEntry(app.packageName, 0));
changed = true;
@@ -996,7 +1033,7 @@
Collections.sort(schedule);
} catch (Exception e) {
- Slog.e(TAG, "Unable to read backup schedule", e);
+ Slog.e(TAG, addUserIdToLogMessage(mUserId, "Unable to read backup schedule"), e);
mFullBackupScheduleFile.delete();
schedule = null;
}
@@ -1052,7 +1089,11 @@
out.write(bufStream.toByteArray());
af.finishWrite(out);
} catch (Exception e) {
- Slog.e(TAG, "Unable to write backup schedule!", e);
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Unable to write backup schedule!"),
+ e);
}
}
}
@@ -1069,12 +1110,17 @@
if (!journal.equals(mJournal)) {
try {
journal.forEach(packageName -> {
- Slog.i(TAG, "Found stale backup journal, scheduling");
- if (MORE_DEBUG) Slog.i(TAG, " " + packageName);
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Found stale backup journal, scheduling"));
+ if (MORE_DEBUG) {
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, " " + packageName));
+ }
dataChangedImpl(packageName);
});
} catch (IOException e) {
- Slog.e(TAG, "Can't read " + journal, e);
+ Slog.e(TAG, addUserIdToLogMessage(mUserId, "Can't read " + journal), e);
}
}
}
@@ -1114,7 +1160,14 @@
boolean isPending, String transportName, String transportDirName) {
synchronized (mQueueLock) {
if (MORE_DEBUG) {
- Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "recordInitPending("
+ + isPending
+ + ") on transport "
+ + transportName));
}
File stateDir = new File(mBaseStateDir, transportDirName);
@@ -1175,8 +1228,17 @@
private void onTransportRegistered(String transportName, String transportDirName) {
if (DEBUG) {
long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
- Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
- + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Transport "
+ + transportName
+ + " registered "
+ + timeMs
+ + "ms after first request (delay = "
+ + INITIALIZATION_DELAY_MILLIS
+ + "ms)"));
}
File stateDir = new File(mBaseStateDir, transportDirName);
@@ -1202,7 +1264,7 @@
private BroadcastReceiver mPackageTrackingReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (MORE_DEBUG) {
- Slog.d(TAG, "Received broadcast " + intent);
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Received broadcast " + intent));
}
String action = intent.getAction();
@@ -1222,24 +1284,33 @@
String packageName = uri.getSchemeSpecificPart();
if (packageName != null) {
- packageList = new String[]{packageName};
+ packageList = new String[] {packageName};
}
changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
if (changed) {
// Look at new transport states for package changed events.
String[] components =
- intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (MORE_DEBUG) {
- Slog.i(TAG, "Package " + packageName + " changed");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Package " + packageName + " changed"));
for (int i = 0; i < components.length; i++) {
- Slog.i(TAG, " * " + components[i]);
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, " * " + components[i]));
}
}
mBackupHandler.post(
- () -> mTransportManager.onPackageChanged(packageName, components));
+ () ->
+ mTransportManager.onPackageChanged(
+ packageName, components));
return;
}
@@ -1261,7 +1332,8 @@
if (added) {
synchronized (mBackupParticipants) {
if (replacing) {
- // Remove the entry under the old uid and fall through to re-add. If an app
+ // Remove the entry under the old uid and fall through to re-add. If
+ // an app
// just opted into key/value backup, add it as a known participant.
removePackageParticipantsLocked(packageList, uid);
}
@@ -1275,13 +1347,15 @@
mPackageManager.getPackageInfoAsUser(
packageName, /* flags */ 0, mUserId);
if (AppBackupUtils.appGetsFullBackup(app)
- && AppBackupUtils.appIsEligibleForBackup(app.applicationInfo,
- mUserId)) {
+ && AppBackupUtils.appIsEligibleForBackup(
+ app.applicationInfo, mUserId)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
- // The app might have just transitioned out of full-data into doing
- // key/value backups, or might have just disabled backups entirely. Make
+ // The app might have just transitioned out of full-data into
+ // doing
+ // key/value backups, or might have just disabled backups
+ // entirely. Make
// sure it is no longer in the full-data queue.
synchronized (mQueueLock) {
dequeueFullBackupLocked(packageName);
@@ -1293,17 +1367,23 @@
() -> mTransportManager.onPackageAdded(packageName));
} catch (NameNotFoundException e) {
if (DEBUG) {
- Slog.w(TAG, "Can't resolve new app " + packageName);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Can't resolve new app " + packageName));
}
}
}
- // Whenever a package is added or updated we need to update the package metadata
+ // Whenever a package is added or updated we need to update the package
+ // metadata
// bookkeeping.
dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
} else {
if (!replacing) {
- // Outright removal. In the full-data case, the app will be dropped from the
+ // Outright removal. In the full-data case, the app will be dropped from
+ // the
// queue when its (now obsolete) name comes up again for backup.
synchronized (mBackupParticipants) {
removePackageParticipantsLocked(packageList, uid);
@@ -1324,12 +1404,19 @@
// Look for apps that define the android:backupAgent attribute
List<PackageInfo> targetApps = allAgentPackages();
if (packageNames != null) {
- if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
+ if (MORE_DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "addPackageParticipantsLocked: #" + packageNames.length));
+ }
for (String packageName : packageNames) {
addPackageParticipantsLockedInner(packageName, targetApps);
}
} else {
- if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
+ if (MORE_DEBUG) {
+ Slog.v(TAG, addUserIdToLogMessage(mUserId, "addPackageParticipantsLocked: all"));
+ }
addPackageParticipantsLockedInner(null, targetApps);
}
}
@@ -1337,7 +1424,10 @@
private void addPackageParticipantsLockedInner(String packageName,
List<PackageInfo> targetPkgs) {
if (MORE_DEBUG) {
- Slog.v(TAG, "Examining " + packageName + " for backup agent");
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Examining " + packageName + " for backup agent"));
}
for (PackageInfo pkg : targetPkgs) {
@@ -1349,10 +1439,15 @@
mBackupParticipants.put(uid, set);
}
set.add(pkg.packageName);
- if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
+ if (MORE_DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "Agent found; added"));
// Schedule a backup for it on general principles
- if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
+ if (MORE_DEBUG) {
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Scheduling backup for new app " + pkg.packageName));
+ }
Message msg = mBackupHandler
.obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
mBackupHandler.sendMessage(msg);
@@ -1363,13 +1458,19 @@
// Remove the given packages' entries from our known active set.
private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
if (packageNames == null) {
- Slog.w(TAG, "removePackageParticipants with null list");
+ Slog.w(TAG, addUserIdToLogMessage(mUserId, "removePackageParticipants with null list"));
return;
}
if (MORE_DEBUG) {
- Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
- + " #" + packageNames.length);
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "removePackageParticipantsLocked: uid="
+ + oldUid
+ + " #"
+ + packageNames.length));
}
for (String pkg : packageNames) {
// Known previous UID, so we know which package set to check
@@ -1377,7 +1478,12 @@
if (set != null && set.contains(pkg)) {
removePackageFromSetLocked(set, pkg);
if (set.isEmpty()) {
- if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set");
+ if (MORE_DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, " last one of this uid; purging set"));
+ }
mBackupParticipants.remove(oldUid);
}
}
@@ -1393,7 +1499,11 @@
// Note that we deliberately leave it 'known' in the "ever backed up"
// bookkeeping so that its current-dataset data will be retrieved
// if the app is subsequently reinstalled
- if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
+ if (MORE_DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(mUserId, " removing participant " + packageName));
+ }
set.remove(packageName);
mPendingBackups.remove(packageName);
}
@@ -1467,14 +1577,19 @@
af.writeInt(-1);
} else {
af.writeInt(mAncestralPackages.size());
- if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
+ if (DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Ancestral packages: " + mAncestralPackages.size()));
+ }
for (String pkgName : mAncestralPackages) {
af.writeUTF(pkgName);
- if (MORE_DEBUG) Slog.v(TAG, " " + pkgName);
+ if (MORE_DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, " " + pkgName));
}
}
} catch (IOException e) {
- Slog.w(TAG, "Unable to write token file:", e);
+ Slog.w(TAG, addUserIdToLogMessage(mUserId, "Unable to write token file:"), e);
}
}
@@ -1487,7 +1602,7 @@
mConnectedAgent = null;
try {
if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId)) {
- Slog.d(TAG, "awaiting agent for " + app);
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "awaiting agent for " + app));
// success; wait for the agent to arrive
// only wait 10 seconds for the bind to happen
@@ -1498,7 +1613,7 @@
mAgentConnectLock.wait(5000);
} catch (InterruptedException e) {
// just bail
- Slog.w(TAG, "Interrupted: " + e);
+ Slog.w(TAG, addUserIdToLogMessage(mUserId, "Interrupted: " + e));
mConnecting = false;
mConnectedAgent = null;
}
@@ -1506,10 +1621,14 @@
// if we timed out with no connect, abort and move on
if (mConnecting) {
- Slog.w(TAG, "Timeout waiting for agent " + app);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Timeout waiting for agent " + app));
mConnectedAgent = null;
}
- if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
+ if (DEBUG) {
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "got agent " + mConnectedAgent));
+ }
agent = mConnectedAgent;
}
} catch (RemoteException e) {
@@ -1575,13 +1694,20 @@
if (!shouldClearData) {
if (MORE_DEBUG) {
- Slog.i(TAG, "Clearing app data is not allowed so not wiping "
- + packageName);
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Clearing app data is not allowed so not wiping "
+ + packageName));
}
return;
}
} catch (NameNotFoundException e) {
- Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Tried to clear data for " + packageName + " but not found"));
return;
}
@@ -1604,13 +1730,22 @@
} catch (InterruptedException e) {
// won't happen, but still.
mClearingData = false;
- Slog.w(TAG, "Interrupted while waiting for " + packageName
- + " data to be cleared", e);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Interrupted while waiting for "
+ + packageName
+ + " data to be cleared"),
+ e);
}
}
if (mClearingData) {
- Slog.w(TAG, "Clearing app data for " + packageName + " timed out");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Clearing app data for " + packageName + " timed out"));
}
}
}
@@ -1627,12 +1762,17 @@
synchronized (mQueueLock) {
if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
if (MORE_DEBUG) {
- Slog.i(TAG, "App in ever-stored, so using current token");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "App in ever-stored, so using current token"));
}
token = mCurrentToken;
}
}
- if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
+ if (MORE_DEBUG) {
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "getAvailableRestoreToken() == " + token));
+ }
return token;
}
@@ -1654,7 +1794,7 @@
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
if (packages == null || packages.length < 1) {
- Slog.e(TAG, "No packages named for backup request");
+ Slog.e(TAG, addUserIdToLogMessage(mUserId, "No packages named for backup request"));
BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
@@ -1665,10 +1805,10 @@
if (!mEnabled || !mSetupComplete) {
Slog.i(
TAG,
- "Backup requested but enabled="
+ addUserIdToLogMessage(mUserId, "Backup requested but enabled="
+ mEnabled
+ " setupComplete="
- + mSetupComplete);
+ + mSetupComplete));
BackupObserverUtils.sendBackupFinished(observer,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
final int logTag = mSetupComplete
@@ -1726,9 +1866,17 @@
EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
fullBackupList.size());
if (MORE_DEBUG) {
- Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
- + fullBackupList.size() + " full backups, " + kvBackupList.size()
- + " k/v backups");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Backup requested for "
+ + packages.length
+ + " packages, of them: "
+ + fullBackupList.size()
+ + " full backups, "
+ + kvBackupList.size()
+ + " k/v backups"));
}
boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
@@ -1744,7 +1892,7 @@
public void cancelBackups() {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
if (MORE_DEBUG) {
- Slog.i(TAG, "cancelBackups() called.");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "cancelBackups() called."));
}
final long oldToken = Binder.clearCallingIdentity();
try {
@@ -1774,13 +1922,27 @@
public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
int operationType) {
if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
- Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
- + Integer.toHexString(token) + " of type " + operationType);
+ Slog.wtf(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "prepareOperationTimeout() doesn't support operation "
+ + Integer.toHexString(token)
+ + " of type "
+ + operationType));
return;
}
if (MORE_DEBUG) {
- Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
- + " interval=" + interval + " callback=" + callback);
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "starting timeout: token="
+ + Integer.toHexString(token)
+ + " interval="
+ + interval
+ + " callback="
+ + callback));
}
synchronized (mCurrentOpLock) {
@@ -1798,8 +1960,12 @@
case OP_TYPE_RESTORE_WAIT:
return MSG_RESTORE_OPERATION_TIMEOUT;
default:
- Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
- + operationType);
+ Slog.wtf(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "getMessageIdForOperationType called on invalid operation type: "
+ + operationType));
return -1;
}
}
@@ -1810,8 +1976,14 @@
*/
public void putOperation(int token, Operation operation) {
if (MORE_DEBUG) {
- Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
- + operation.type);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Adding operation token="
+ + Integer.toHexString(token)
+ + ", operation type="
+ + operation.type));
}
synchronized (mCurrentOpLock) {
mCurrentOperations.put(token, operation);
@@ -1824,12 +1996,15 @@
*/
public void removeOperation(int token) {
if (MORE_DEBUG) {
- Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Removing operation token=" + Integer.toHexString(token)));
}
synchronized (mCurrentOpLock) {
if (mCurrentOperations.get(token) == null) {
- Slog.w(TAG, "Duplicate remove for operation. token="
- + Integer.toHexString(token));
+ Slog.w(TAG, addUserIdToLogMessage(mUserId, "Duplicate remove for operation. token="
+ + Integer.toHexString(token)));
}
mCurrentOperations.remove(token);
}
@@ -1838,8 +2013,8 @@
/** Block until we received an operation complete message (from the agent or cancellation). */
public boolean waitUntilOperationComplete(int token) {
if (MORE_DEBUG) {
- Slog.i(TAG, "Blocking until operation complete for "
- + Integer.toHexString(token));
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Blocking until operation complete for "
+ + Integer.toHexString(token)));
}
int finalState = OP_PENDING;
Operation op = null;
@@ -1858,8 +2033,12 @@
// When the wait is notified we loop around and recheck the current state
} else {
if (MORE_DEBUG) {
- Slog.d(TAG, "Unblocked waiting for operation token="
- + Integer.toHexString(token));
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Unblocked waiting for operation token="
+ + Integer.toHexString(token)));
}
// No longer pending; we're done
finalState = op.state;
@@ -1874,8 +2053,8 @@
mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
}
if (MORE_DEBUG) {
- Slog.v(TAG, "operation " + Integer.toHexString(token)
- + " complete: finalState=" + finalState);
+ Slog.v(TAG, addUserIdToLogMessage(mUserId, "operation " + Integer.toHexString(token)
+ + " complete: finalState=" + finalState));
}
return finalState == OP_ACKNOWLEDGED;
}
@@ -1888,21 +2067,31 @@
op = mCurrentOperations.get(token);
if (MORE_DEBUG) {
if (op == null) {
- Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
- + " but no op found");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Cancel of token "
+ + Integer.toHexString(token)
+ + " but no op found"));
}
}
int state = (op != null) ? op.state : OP_TIMEOUT;
if (state == OP_ACKNOWLEDGED) {
// The operation finished cleanly, so we have nothing more to do.
if (DEBUG) {
- Slog.w(TAG, "Operation already got an ack."
- + "Should have been removed from mCurrentOperations.");
+ Slog.w(TAG, addUserIdToLogMessage(mUserId, "Operation already got an ack."
+ + "Should have been removed from mCurrentOperations."));
}
op = null;
mCurrentOperations.delete(token);
} else if (state == OP_PENDING) {
- if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
+ if (DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Cancel: token=" + Integer.toHexString(token)));
+ }
op.state = OP_TIMEOUT;
// Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
// called after we receive cancel here. We need this op's state there.
@@ -1920,7 +2109,7 @@
// If there's a TimeoutHandler for this event, call it
if (op != null && op.callback != null) {
if (MORE_DEBUG) {
- Slog.v(TAG, " Invoking cancel on " + op.callback);
+ Slog.v(TAG, addUserIdToLogMessage(mUserId, " Invoking cancel on " + op.callback));
}
op.callback.handleCancel(cancelAll);
}
@@ -1955,13 +2144,20 @@
// manifest flag! TODO something less direct.
if (!UserHandle.isCore(app.uid)
&& !app.packageName.equals("com.android.backupconfirm")) {
- if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
+ if (MORE_DEBUG) {
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Killing agent host process"));
+ }
mActivityManager.killApplicationProcess(app.processName, app.uid);
} else {
- if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
+ if (MORE_DEBUG) {
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Not killing after operation: " + app.processName));
+ }
}
} catch (RemoteException e) {
- Slog.d(TAG, "Lost app trying to shut down");
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Lost app trying to shut down"));
}
}
@@ -1975,7 +2171,12 @@
} catch (Exception e) {
// If we can't talk to the storagemanager service we have a serious problem; fail
// "secure" i.e. assuming that the device is encrypted.
- Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Unable to communicate with storagemanager service: "
+ + e.getMessage()));
return true;
}
}
@@ -1999,7 +2200,10 @@
FullBackupJob.schedule(mUserId, mContext, latency, mConstants);
} else {
if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Full backup queue empty; not scheduling");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Full backup queue empty; not scheduling"));
}
}
}
@@ -2054,7 +2258,10 @@
private boolean fullBackupAllowable(String transportName) {
if (!mTransportManager.isTransportRegistered(transportName)) {
- Slog.w(TAG, "Transport not registered; full data backup not performed");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Transport not registered; full data backup not performed"));
return false;
}
@@ -2066,12 +2273,19 @@
File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
if (pmState.length() <= 0) {
if (DEBUG) {
- Slog.i(TAG, "Full backup requested but dataset not yet initialized");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Full backup requested but dataset not yet initialized"));
}
return false;
}
} catch (Exception e) {
- Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Unable to get transport name: " + e.getMessage()));
return false;
}
@@ -2104,8 +2318,8 @@
// the job driving automatic backups; that job will be scheduled again when
// the user enables backup.
if (MORE_DEBUG) {
- Slog.i(TAG, "beginFullBackup but enabled=" + mEnabled
- + " setupComplete=" + mSetupComplete + "; ignoring");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "beginFullBackup but enabled=" + mEnabled
+ + " setupComplete=" + mSetupComplete + "; ignoring"));
}
return false;
}
@@ -2115,19 +2329,29 @@
final PowerSaveState result =
mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
if (result.batterySaverEnabled) {
- if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
+ if (DEBUG) {
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Deferring scheduled full backups in battery saver mode"));
+ }
FullBackupJob.schedule(mUserId, mContext, keyValueBackupInterval, mConstants);
return false;
}
if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Beginning scheduled full backup operation");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Beginning scheduled full backup operation"));
}
// Great; we're able to run full backup jobs now. See if we have any work to do.
synchronized (mQueueLock) {
if (mRunningFullBackupTask != null) {
- Slog.e(TAG, "Backup triggered but one already/still running!");
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Backup triggered but one already/still running!"));
return false;
}
@@ -2143,7 +2367,10 @@
if (mFullBackupQueue.size() == 0) {
// no work to do so just bow out
if (DEBUG) {
- Slog.i(TAG, "Backup queue empty; doing nothing");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Backup queue empty; doing nothing"));
}
runBackup = false;
break;
@@ -2154,7 +2381,10 @@
String transportName = mTransportManager.getCurrentTransportName();
if (!fullBackupAllowable(transportName)) {
if (MORE_DEBUG) {
- Slog.i(TAG, "Preconditions not met; not running full backup");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Preconditions not met; not running full backup"));
}
runBackup = false;
// Typically this means we haven't run a key/value backup yet. Back off
@@ -2170,7 +2400,11 @@
if (!runBackup) {
// It's too early to back up the next thing in the queue, so bow out
if (MORE_DEBUG) {
- Slog.i(TAG, "Device ready but too early to back up next app");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Device ready but too early to back up next app"));
}
// Wait until the next app in the queue falls due for a full data backup
latency = fullBackupInterval - timeSinceRun;
@@ -2185,8 +2419,14 @@
// so we cull it and force a loop around to consider the new head
// app.
if (MORE_DEBUG) {
- Slog.i(TAG, "Culling package " + entry.packageName
- + " in full-backup queue but not eligible");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Culling package "
+ + entry.packageName
+ + " in full-backup queue but not"
+ + " eligible"));
}
mFullBackupQueue.remove(0);
headBusy = true; // force the while() condition
@@ -2204,9 +2444,14 @@
+ mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
if (DEBUG_SCHEDULING) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Slog.i(TAG, "Full backup time but " + entry.packageName
- + " is busy; deferring to "
- + sdf.format(new Date(nextEligible)));
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Full backup time but "
+ + entry.packageName
+ + " is busy; deferring to "
+ + sdf.format(new Date(nextEligible))));
}
// This relocates the app's entry from the head of the queue to
// its order-appropriate position further down, so upon looping
@@ -2225,7 +2470,11 @@
if (!runBackup) {
if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Nothing pending full backup; rescheduling +" + latency));
}
final long deferTime = latency; // pin for the closure
FullBackupJob.schedule(mUserId, mContext, deferTime, mConstants);
@@ -2273,7 +2522,10 @@
}
if (pftbt != null) {
if (DEBUG_SCHEDULING) {
- Slog.i(TAG, "Telling running backup to stop");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Telling running backup to stop"));
}
pftbt.handleCancel(true);
}
@@ -2286,7 +2538,7 @@
public void restoreWidgetData(String packageName, byte[] widgetData) {
// Apply the restored widget state and generate the ID update for the app
if (MORE_DEBUG) {
- Slog.i(TAG, "Incorporating restored widget data");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Incorporating restored widget data"));
}
AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, mUserId);
}
@@ -2306,8 +2558,15 @@
// may share a uid, we need to note all candidates within that uid and schedule
// a backup pass for each of them.
if (targets == null) {
- Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
- + " uid=" + Binder.getCallingUid());
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "dataChanged but no participant pkg='"
+ + packageName
+ + "'"
+ + " uid="
+ + Binder.getCallingUid()));
return;
}
@@ -2318,7 +2577,12 @@
// one already there, then overwrite it, but no harm done.
BackupRequest req = new BackupRequest(packageName);
if (mPendingBackups.put(packageName, req) == null) {
- if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
+ if (MORE_DEBUG) {
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Now staging backup of " + packageName));
+ }
// Journal this request in case of crash. The put()
// operation returned null when this package was not already
@@ -2358,7 +2622,10 @@
if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
mJournal.addPackage(str);
} catch (IOException e) {
- Slog.e(TAG, "Can't write " + str + " to backup journal", e);
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Can't write " + str + " to backup journal"),
+ e);
mJournal = null;
}
}
@@ -2369,8 +2636,15 @@
public void dataChanged(final String packageName) {
final HashSet<String> targets = dataChangedTargets(packageName);
if (targets == null) {
- Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
- + " uid=" + Binder.getCallingUid());
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "dataChanged but no participant pkg='"
+ + packageName
+ + "'"
+ + " uid="
+ + Binder.getCallingUid()));
return;
}
@@ -2385,7 +2659,10 @@
public void initializeTransports(String[] transportNames, IBackupObserver observer) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"initializeTransport");
- Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "initializeTransport(): " + Arrays.asList(transportNames)));
final long oldId = Binder.clearCallingIdentity();
try {
@@ -2404,11 +2681,18 @@
public void setAncestralSerialNumber(long ancestralSerialNumber) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
"setAncestralSerialNumber");
- Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber);
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Setting ancestral work profile id to " + ancestralSerialNumber));
try (RandomAccessFile af = getAncestralSerialNumberFile()) {
af.writeLong(ancestralSerialNumber);
} catch (IOException e) {
- Slog.w(TAG, "Unable to write to work profile serial mapping file:", e);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Unable to write to work profile serial mapping file:"),
+ e);
}
}
@@ -2420,7 +2704,11 @@
try (RandomAccessFile af = getAncestralSerialNumberFile()) {
return af.readLong();
} catch (IOException e) {
- Slog.w(TAG, "Unable to write to work profile serial number file:", e);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Unable to write to work profile serial number file:"),
+ e);
return -1;
}
}
@@ -2443,13 +2731,24 @@
/** Clear the given package's backup data from the current transport. */
public void clearBackupData(String transportName, String packageName) {
- if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
+ if (DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "clearBackupData() of " + packageName + " on " + transportName));
+ }
+
PackageInfo info;
try {
info = mPackageManager.getPackageInfoAsUser(packageName,
PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
} catch (NameNotFoundException e) {
- Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "No such package '" + packageName + "' - not clearing backup data"));
return;
}
@@ -2462,13 +2761,22 @@
} else {
// a caller with full permission can ask to back up any participating app
// !!! TODO: allow data-clear of ANY app?
- if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
+ if (MORE_DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Privileged caller, allowing clear of other apps"));
+ }
apps = mProcessedPackagesJournal.getPackagesCopy();
}
if (apps.contains(packageName)) {
// found it; fire off the clear request
- if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
+ if (MORE_DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Found the app - running clear process"));
+ }
mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
synchronized (mQueueLock) {
TransportClient transportClient =
@@ -2507,24 +2815,36 @@
final PowerSaveState result =
mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
if (result.batterySaverEnabled) {
- if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
+ if (DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Not running backup while in battery save mode"));
+ }
// Try again in several hours.
KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
} else {
- if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
+ if (DEBUG) {
+ Slog.v(TAG, addUserIdToLogMessage(mUserId, "Scheduling immediate backup pass"));
+ }
synchronized (getQueueLock()) {
if (getPendingInits().size() > 0) {
// If there are pending init operations, we process those and then settle
// into the usual periodic backup schedule.
if (MORE_DEBUG) {
- Slog.v(TAG, "Init pending at scheduled backup");
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Init pending at scheduled backup"));
}
try {
getAlarmManager().cancel(mRunInitIntent);
mRunInitIntent.send();
} catch (PendingIntent.CanceledException ce) {
- Slog.w(TAG, "Run init intent cancelled");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Run init intent cancelled"));
}
return;
}
@@ -2534,8 +2854,8 @@
if (!isEnabled() || !isSetupComplete()) {
Slog.w(
TAG,
- "Backup pass but enabled=" + isEnabled()
- + " setupComplete=" + isSetupComplete());
+ addUserIdToLogMessage(mUserId, "Backup pass but enabled=" + isEnabled()
+ + " setupComplete=" + isSetupComplete()));
return;
}
@@ -2582,16 +2902,31 @@
long oldId = Binder.clearCallingIdentity();
try {
if (!mSetupComplete) {
- Slog.i(TAG, "Backup not supported before setup");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Backup not supported before setup"));
return;
}
if (DEBUG) {
- Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
- + " shared=" + includeShared + " all=" + doAllApps + " system="
- + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Requesting backup: apks="
+ + includeApks
+ + " obb="
+ + includeObbs
+ + " shared="
+ + includeShared
+ + " all="
+ + doAllApps
+ + " system="
+ + includeSystem
+ + " includekeyvalue="
+ + doKeyValue
+ + " pkgs="
+ + pkgList));
}
- Slog.i(TAG, "Beginning adb backup...");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning adb backup..."));
AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
@@ -2602,9 +2937,16 @@
}
// start up the confirmation UI
- if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Starting backup confirmation UI, token=" + token));
+ }
if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
- Slog.e(TAG, "Unable to launch backup confirmation UI");
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Unable to launch backup confirmation UI"));
mAdbBackupRestoreConfirmations.delete(token);
return;
}
@@ -2618,16 +2960,22 @@
startConfirmationTimeout(token, params);
// wait for the backup to be performed
- if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
+ if (DEBUG) {
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Waiting for backup completion..."));
+ }
waitForCompletion(params);
} finally {
try {
fd.close();
} catch (IOException e) {
- Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "IO error closing output for adb backup: " + e.getMessage()));
}
Binder.restoreCallingIdentity(oldId);
- Slog.d(TAG, "Adb backup processing complete.");
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Adb backup processing complete."));
}
}
@@ -2644,10 +2992,14 @@
String transportName = mTransportManager.getCurrentTransportName();
if (!fullBackupAllowable(transportName)) {
- Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Full backup not currently possible -- key/value backup not yet run?"));
} else {
if (DEBUG) {
- Slog.d(TAG, "fullTransportBackup()");
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "fullTransportBackup()"));
}
final long oldId = Binder.clearCallingIdentity();
@@ -2687,7 +3039,7 @@
}
if (DEBUG) {
- Slog.d(TAG, "Done with full transport backup.");
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Done with full transport backup."));
}
}
@@ -2707,11 +3059,13 @@
try {
if (!mSetupComplete) {
- Slog.i(TAG, "Full restore not permitted before setup");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Full restore not permitted before setup"));
return;
}
- Slog.i(TAG, "Beginning restore...");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning restore..."));
AdbRestoreParams params = new AdbRestoreParams(fd);
final int token = generateRandomIntegerToken();
@@ -2720,9 +3074,16 @@
}
// start up the confirmation UI
- if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Starting restore confirmation UI, token=" + token));
+ }
if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
- Slog.e(TAG, "Unable to launch restore confirmation");
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Unable to launch restore confirmation"));
mAdbBackupRestoreConfirmations.delete(token);
return;
}
@@ -2736,16 +3097,21 @@
startConfirmationTimeout(token, params);
// wait for the restore to be performed
- if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
+ if (DEBUG) {
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Waiting for restore completion..."));
+ }
waitForCompletion(params);
} finally {
try {
fd.close();
} catch (IOException e) {
- Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Error trying to close fd after adb restore: " + e));
}
Binder.restoreCallingIdentity(oldId);
- Slog.i(TAG, "adb restore processing complete.");
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "adb restore processing complete."));
}
}
@@ -2773,8 +3139,8 @@
private void startConfirmationTimeout(int token, AdbParams params) {
if (MORE_DEBUG) {
- Slog.d(TAG, "Posting conf timeout msg after "
- + TIMEOUT_FULL_CONFIRMATION + " millis");
+ Slog.d(TAG, addUserIdToLogMessage(mUserId, "Posting conf timeout msg after "
+ + TIMEOUT_FULL_CONFIRMATION + " millis"));
}
Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
token, 0, params);
@@ -2806,8 +3172,11 @@
public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
if (DEBUG) {
- Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
- + " allow=" + allow);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "acknowledgeAdbBackupOrRestore : token=" + token + " allow=" + allow));
}
// TODO: possibly require not just this signature-only permission, but even
@@ -2835,17 +3204,29 @@
params.encryptPassword = encPpassword;
- if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
+ if (MORE_DEBUG) {
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Sending conf message with verb " + verb));
+ }
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(verb, params);
mBackupHandler.sendMessage(msg);
} else {
- Slog.w(TAG, "User rejected full backup/restore operation");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "User rejected full backup/restore operation"));
// indicate completion without having actually transferred any data
signalAdbBackupRestoreCompletion(params);
}
} else {
- Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Attempted to ack full backup/restore with invalid token"));
}
}
} finally {
@@ -2858,7 +3239,7 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
- Slog.i(TAG, "Backup enabled => " + enable);
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Backup enabled => " + enable));
long oldId = Binder.clearCallingIdentity();
try {
@@ -2875,7 +3256,9 @@
scheduleNextFullBackupJob(0);
} else if (!enable) {
// No longer enabled, so stop running backups
- if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
+ if (MORE_DEBUG) {
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Opting out of backup"));
+ }
KeyValueBackupJob.cancel(mUserId, mContext);
@@ -2891,12 +3274,15 @@
name -> {
final String dirName;
try {
- dirName =
- mTransportManager
- .getTransportDirName(name);
+ dirName = mTransportManager.getTransportDirName(name);
} catch (TransportNotRegisteredException e) {
// Should never happen
- Slog.e(TAG, "Unexpected unregistered transport", e);
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Unexpected unregistered transport"),
+ e);
return;
}
transportNames.add(name);
@@ -2925,7 +3311,7 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setAutoRestore");
- Slog.i(TAG, "Auto restore => " + doAutoRestore);
+ Slog.i(TAG, addUserIdToLogMessage(mUserId, "Auto restore => " + doAutoRestore));
final long oldId = Binder.clearCallingIdentity();
try {
@@ -2951,7 +3337,12 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getCurrentTransport");
String currentTransport = mTransportManager.getCurrentTransportName();
- if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
+ if (MORE_DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "... getCurrentTransport() returning " + currentTransport));
+ }
return currentTransport;
}
@@ -3090,8 +3481,14 @@
try {
String previousTransportName = mTransportManager.selectTransport(transportName);
updateStateForTransport(transportName);
- Slog.v(TAG, "selectBackupTransport(transport = " + transportName
- + "): previous transport = " + previousTransportName);
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "selectBackupTransport(transport = "
+ + transportName
+ + "): previous transport = "
+ + previousTransportName));
return previousTransportName;
} finally {
Binder.restoreCallingIdentity(oldId);
@@ -3110,7 +3507,11 @@
final long oldId = Binder.clearCallingIdentity();
try {
String transportString = transportComponent.flattenToShortString();
- Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "selectBackupTransportAsync(transport = " + transportString + ")"));
mBackupHandler.post(
() -> {
String transportName = null;
@@ -3122,7 +3523,10 @@
mTransportManager.getTransportName(transportComponent);
updateStateForTransport(transportName);
} catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Transport got unregistered");
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Transport got unregistered"));
result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
}
}
@@ -3134,7 +3538,12 @@
listener.onFailure(result);
}
} catch (RemoteException e) {
- Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "ISelectBackupTransportCallback listener not"
+ + " available"));
}
});
} finally {
@@ -3159,11 +3568,23 @@
// Oops. We can't know the current dataset token, so reset and figure it out
// when we do the next k/v backup operation on this transport.
mCurrentToken = 0;
- Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Transport "
+ + newTransportName
+ + " not available: current token = 0"));
}
mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
} else {
- Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Transport "
+ + newTransportName
+ + " not registered: current token = 0"));
// The named transport isn't registered, so we can't know what its current dataset token
// is. Reset as above.
mCurrentToken = 0;
@@ -3181,11 +3602,19 @@
try {
Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
if (MORE_DEBUG) {
- Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "getConfigurationIntent() returning intent " + intent));
}
return intent;
} catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Unable to get configuration intent from transport: "
+ + e.getMessage()));
return null;
}
}
@@ -3206,11 +3635,18 @@
try {
String string = mTransportManager.getTransportCurrentDestinationString(transportName);
if (MORE_DEBUG) {
- Slog.d(TAG, "getDestinationString() returning " + string);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "getDestinationString() returning " + string));
}
return string;
} catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Unable to get destination string from transport: " + e.getMessage()));
return null;
}
}
@@ -3223,11 +3659,18 @@
try {
Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
if (MORE_DEBUG) {
- Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "getDataManagementIntent() returning intent " + intent));
}
return intent;
} catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Unable to get management intent from transport: " + e.getMessage()));
return null;
}
}
@@ -3243,11 +3686,18 @@
try {
CharSequence label = mTransportManager.getTransportDataManagementLabel(transportName);
if (MORE_DEBUG) {
- Slog.d(TAG, "getDataManagementLabel() returning " + label);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "getDataManagementLabel() returning " + label));
}
return label;
} catch (TransportNotRegisteredException e) {
- Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Unable to get management label from transport: " + e.getMessage()));
return null;
}
}
@@ -3259,12 +3709,21 @@
public void agentConnected(String packageName, IBinder agentBinder) {
synchronized (mAgentConnectLock) {
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "agentConnected pkg=" + packageName + " agent=" + agentBinder));
mConnectedAgent = IBackupAgent.Stub.asInterface(agentBinder);
mConnecting = false;
} else {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " claiming agent connected");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Non-system process uid="
+ + Binder.getCallingUid()
+ + " claiming agent connected"));
}
mAgentConnectLock.notifyAll();
}
@@ -3282,8 +3741,13 @@
mConnectedAgent = null;
mConnecting = false;
} else {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " claiming agent disconnected");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Non-system process uid="
+ + Binder.getCallingUid()
+ + " claiming agent disconnected"));
}
mAgentConnectLock.notifyAll();
}
@@ -3295,8 +3759,13 @@
*/
public void restoreAtInstall(String packageName, int token) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
- + " attemping install-time restore");
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Non-system process uid="
+ + Binder.getCallingUid()
+ + " attemping install-time restore"));
return;
}
@@ -3304,25 +3773,35 @@
long restoreSet = getAvailableRestoreToken(packageName);
if (DEBUG) {
- Slog.v(TAG, "restoreAtInstall pkg=" + packageName
- + " token=" + Integer.toHexString(token)
- + " restoreSet=" + Long.toHexString(restoreSet));
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "restoreAtInstall pkg="
+ + packageName
+ + " token="
+ + Integer.toHexString(token)
+ + " restoreSet="
+ + Long.toHexString(restoreSet)));
}
if (restoreSet == 0) {
- if (MORE_DEBUG) Slog.i(TAG, "No restore set");
+ if (MORE_DEBUG) Slog.i(TAG, addUserIdToLogMessage(mUserId, "No restore set"));
skip = true;
}
TransportClient transportClient =
mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
if (transportClient == null) {
- if (DEBUG) Slog.w(TAG, "No transport client");
+ if (DEBUG) Slog.w(TAG, addUserIdToLogMessage(mUserId, "No transport client"));
skip = true;
}
if (!mAutoRestore) {
if (DEBUG) {
- Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Non-restorable state: auto=" + mAutoRestore));
}
skip = true;
}
@@ -3341,7 +3820,9 @@
};
if (MORE_DEBUG) {
- Slog.d(TAG, "Restore at install of " + packageName);
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(mUserId, "Restore at install of " + packageName));
}
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj =
@@ -3356,7 +3837,10 @@
mBackupHandler.sendMessage(msg);
} catch (Exception e) {
// Calling into the transport broke; back off and proceed with the installation.
- Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
+ Slog.e(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Unable to contact transport: " + e.getMessage()));
skip = true;
}
}
@@ -3370,7 +3854,7 @@
}
// Tell the PackageManager to proceed with the post-install handling for this package.
- if (DEBUG) Slog.v(TAG, "Finishing install immediately");
+ if (DEBUG) Slog.v(TAG, addUserIdToLogMessage(mUserId, "Finishing install immediately"));
try {
mPackageManagerBinder.finishPackageInstall(token, false);
} catch (RemoteException e) { /* can't happen */ }
@@ -3380,8 +3864,11 @@
/** Hand off a restore session. */
public IRestoreSession beginRestoreSession(String packageName, String transport) {
if (DEBUG) {
- Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
- + " transport=" + transport);
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "beginRestoreSession: pkg=" + packageName + " transport=" + transport));
}
boolean needPermission = true;
@@ -3393,7 +3880,10 @@
try {
app = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
} catch (NameNotFoundException nnf) {
- Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Asked to restore nonexistent pkg " + packageName));
throw new IllegalArgumentException("Package " + packageName + " not found");
}
@@ -3407,19 +3897,32 @@
}
if (needPermission) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "beginRestoreSession");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BACKUP, "beginRestoreSession");
} else {
- if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
+ if (DEBUG) {
+ Slog.d(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "restoring self on current transport; no permission needed"));
+ }
}
synchronized (this) {
if (mActiveRestoreSession != null) {
- Slog.i(TAG, "Restore session requested but one already active");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Restore session requested but one already active"));
return null;
}
if (mBackupRunning) {
- Slog.i(TAG, "Restore session requested but currently running backups");
+ Slog.i(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Restore session requested but currently running backups"));
return null;
}
mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
@@ -3433,9 +3936,14 @@
public void clearRestoreSession(ActiveRestoreSession currentSession) {
synchronized (this) {
if (currentSession != mActiveRestoreSession) {
- Slog.e(TAG, "ending non-current restore session");
+ Slog.e(TAG, addUserIdToLogMessage(mUserId, "ending non-current restore session"));
} else {
- if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
+ if (DEBUG) {
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId, "Clearing restore session and halting timeout"));
+ }
mActiveRestoreSession = null;
mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
}
@@ -3448,7 +3956,11 @@
*/
public void opComplete(int token, long result) {
if (MORE_DEBUG) {
- Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
+ Slog.v(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "opComplete: " + Integer.toHexString(token) + " result=" + result));
}
Operation op = null;
synchronized (mCurrentOpLock) {
@@ -3461,8 +3973,12 @@
mCurrentOperations.delete(token);
} else if (op.state == OP_ACKNOWLEDGED) {
if (DEBUG) {
- Slog.w(TAG, "Received duplicate ack for token="
- + Integer.toHexString(token));
+ Slog.w(
+ TAG,
+ addUserIdToLogMessage(
+ mUserId,
+ "Received duplicate ack for token="
+ + Integer.toHexString(token)));
}
op = null;
mCurrentOperations.remove(token);
@@ -3606,7 +4122,7 @@
" " + f.getName() + " - " + f.length() + " state bytes");
}
} catch (Exception e) {
- Slog.e(TAG, "Error in transport", e);
+ Slog.e(TAG, addUserIdToLogMessage(mUserId, "Error in transport"), e);
pw.println(" Error: " + e);
}
}
@@ -3665,6 +4181,10 @@
}
}
+ private static String addUserIdToLogMessage(int userId, String message) {
+ return "[UserID:" + userId + "] " + message;
+ }
+
public IBackupManager getBackupManagerBinder() {
return mBackupManagerBinder;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 942d563..9e19ad2 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -98,9 +98,9 @@
"android.hardware.power-V1.0-java",
"android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
+ "android.net.ipsec.ike.stubs.module_libs_api",
"app-compat-annotations",
"framework-tethering-stubs-module_libs_api",
- "ike-stubs",
],
required: [
@@ -128,7 +128,6 @@
"android.hidl.manager-V1.2-java",
"dnsresolver_aidl_interface-V2-java",
"netd_event_listener_interface-java",
- "ike-stubs",
"overlayable_policy_aidl-java",
],
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index f006d68..7d85966 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -989,4 +989,9 @@
* @param enabled true if visibility blocks should be logged
*/
public abstract void setVisibilityLogging(String packageName, boolean enabled);
+
+ /**
+ * Returns if a package name is a valid system package.
+ */
+ public abstract boolean isSystemPackage(@NonNull String packageName);
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 7840b19..9b04e79 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1672,7 +1672,7 @@
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- intent.putExtra("time-zone", zone.getID());
+ intent.putExtra(Intent.EXTRA_TIMEZONE, zone.getID());
getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7287a44..deae459 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2083,9 +2083,9 @@
}
private void enforceNetworkFactoryPermission() {
- mContext.enforceCallingOrSelfPermission(
+ enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_FACTORY,
- "ConnectivityService");
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkSettingsPermission() {
@@ -7803,18 +7803,21 @@
private void handleNetworkTestedWithExtras(
@NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
final NetworkAgentInfo nai = reportEvent.mNai;
+ final NetworkCapabilities networkCapabilities =
+ new NetworkCapabilities(nai.networkCapabilities);
+ clearNetworkCapabilitiesUids(networkCapabilities);
final ConnectivityReport report =
new ConnectivityReport(
reportEvent.mNai.network,
reportEvent.mTimestampMillis,
nai.linkProperties,
- nai.networkCapabilities,
+ networkCapabilities,
extras);
final List<IConnectivityDiagnosticsCallback> results =
getMatchingPermissionedCallbacks(nai);
for (final IConnectivityDiagnosticsCallback cb : results) {
try {
- cb.onConnectivityReport(report);
+ cb.onConnectivityReportAvailable(report);
} catch (RemoteException ex) {
loge("Error invoking onConnectivityReport", ex);
}
@@ -7824,13 +7827,16 @@
private void handleDataStallSuspected(
@NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
@NonNull PersistableBundle extras) {
+ final NetworkCapabilities networkCapabilities =
+ new NetworkCapabilities(nai.networkCapabilities);
+ clearNetworkCapabilitiesUids(networkCapabilities);
final DataStallReport report =
new DataStallReport(
nai.network,
timestampMillis,
detectionMethod,
nai.linkProperties,
- nai.networkCapabilities,
+ networkCapabilities,
extras);
final List<IConnectivityDiagnosticsCallback> results =
getMatchingPermissionedCallbacks(nai);
@@ -7856,6 +7862,12 @@
}
}
+ private void clearNetworkCapabilitiesUids(@NonNull NetworkCapabilities nc) {
+ nc.setUids(null);
+ nc.setAdministratorUids(Collections.EMPTY_LIST);
+ nc.setOwnerUid(Process.INVALID_UID);
+ }
+
private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
@NonNull NetworkAgentInfo nai) {
final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
@@ -7880,8 +7892,15 @@
return true;
}
- if (!mLocationPermissionChecker.checkLocationPermission(
- callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+ // LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid
+ // and package name don't match. Throwing on the CS thread is not acceptable, so wrap the
+ // call in a try-catch.
+ try {
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+ return false;
+ }
+ } catch (SecurityException e) {
return false;
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 318a030..191a9bc 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -22,13 +22,9 @@
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.gsi.IGsiServiceCallback;
-import android.gsi.IGsid;
import android.os.Environment;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.IDynamicSystemService;
@@ -42,9 +38,8 @@
* DynamicSystemService implements IDynamicSystemService. It provides permission check before
* passing requests to gsid
*/
-public class DynamicSystemService extends IDynamicSystemService.Stub implements DeathRecipient {
+public class DynamicSystemService extends IDynamicSystemService.Stub {
private static final String TAG = "DynamicSystemService";
- private static final String NO_SERVICE_ERROR = "no gsiservice";
private static final int GSID_ROUGH_TIMEOUT_MS = 8192;
private static final String PATH_DEFAULT = "/data/gsi/";
private Context mContext;
@@ -55,57 +50,12 @@
mContext = context;
}
- private static IGsiService connect(DeathRecipient recipient) throws RemoteException {
- IBinder binder = ServiceManager.getService("gsiservice");
- if (binder == null) {
- return null;
- }
- /**
- * The init will restart gsiservice if it crashed and the proxy object will need to be
- * re-initialized in this case.
- */
- binder.linkToDeath(recipient, 0);
-
- IGsid gsid = IGsid.Stub.asInterface(binder);
- return gsid.getClient();
- }
-
- /** implements DeathRecipient */
- @Override
- public void binderDied() {
- Slog.w(TAG, "gsiservice died; reconnecting");
- synchronized (this) {
- mGsiService = null;
- }
- }
-
private IGsiService getGsiService() throws RemoteException {
checkPermission();
-
- if (!"running".equals(SystemProperties.get("init.svc.gsid"))) {
- SystemProperties.set("ctl.start", "gsid");
+ if (mGsiService != null) {
+ return mGsiService;
}
-
- for (int sleepMs = 64; sleepMs <= (GSID_ROUGH_TIMEOUT_MS << 1); sleepMs <<= 1) {
- synchronized (this) {
- if (mGsiService == null) {
- mGsiService = connect(this);
- }
- if (mGsiService != null) {
- return mGsiService;
- }
- }
-
- try {
- Slog.d(TAG, "GsiService is not ready, wait for " + sleepMs + "ms");
- Thread.sleep(sleepMs);
- } catch (InterruptedException e) {
- Slog.e(TAG, "Interrupted when waiting for GSID");
- return null;
- }
- }
-
- throw new RemoteException(NO_SERVICE_ERROR);
+ return IGsiService.Stub.asInterface(waitForService("gsiservice"));
}
private void checkPermission() {
@@ -133,6 +83,7 @@
@Override
public boolean startInstallation(String dsuSlot) throws RemoteException {
IGsiService service = getGsiService();
+ mGsiService = service;
// priority from high to low: sysprop -> sdcard -> /data
String path = SystemProperties.get("os.aot.path");
if (path.isEmpty()) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index acd4039..d814b9c 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -200,7 +200,7 @@
// time
private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
- private static final String FEATURE_ID = "LocationService";
+ private static final String ATTRIBUTION_TAG = "LocationService";
private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
@@ -246,7 +246,7 @@
private int mBatterySaverMode;
private LocationManagerService(Context context) {
- mContext = context.createFeatureContext(FEATURE_ID);
+ mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mHandler = FgThread.getHandler();
mLocalService = new LocalService();
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 2cfe404..41a104c 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -177,6 +177,9 @@
// 0 if no prune is scheduled.
@GuardedBy("mLock")
private long mUptimeAtLastStateSync;
+ // If true, sync explicit health check packages with the ExplicitHealthCheckController.
+ @GuardedBy("mLock")
+ private boolean mSyncRequired = false;
@FunctionalInterface
@VisibleForTesting
@@ -252,6 +255,7 @@
*/
public void registerHealthObserver(PackageHealthObserver observer) {
synchronized (mLock) {
+ mSyncRequired = true;
ObserverInternal internalObserver = mAllObservers.get(observer.getName());
if (internalObserver != null) {
internalObserver.registeredObserver = observer;
@@ -357,6 +361,10 @@
*/
public void onPackageFailure(List<VersionedPackage> packages,
@FailureReasons int failureReason) {
+ if (packages == null) {
+ Slog.w(TAG, "Could not resolve a list of failing packages");
+ return;
+ }
mLongTaskHandler.post(() -> {
synchronized (mLock) {
if (mAllObservers.isEmpty()) {
@@ -631,7 +639,7 @@
synchronized (mLock) {
if (mIsPackagesReady) {
Set<String> packages = getPackagesPendingHealthChecksLocked();
- if (!packages.equals(mRequestedHealthCheckPackages)) {
+ if (!packages.equals(mRequestedHealthCheckPackages) || mSyncRequired) {
syncRequired = true;
mRequestedHealthCheckPackages = packages;
}
@@ -643,6 +651,7 @@
Slog.i(TAG, "Syncing health check requests for packages: "
+ mRequestedHealthCheckPackages);
mHealthCheckController.syncRequests(mRequestedHealthCheckPackages);
+ mSyncRequired = false;
}
}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 80036bb..808d322 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -99,6 +99,8 @@
private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
+ private static final String DEVICE_CONFIG_DISABLE_FLAG = "disable_rescue_party";
+
private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
| ApplicationInfo.FLAG_SYSTEM;
@@ -114,6 +116,14 @@
return false;
}
+ // We're disabled if the DeviceConfig disable flag is set to true.
+ // This is in case that an emergency rollback of the feature is needed.
+ if (DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CONFIGURATION, DEVICE_CONFIG_DISABLE_FLAG, false)) {
+ Slog.v(TAG, "Disabled because of DeviceConfig flag");
+ return true;
+ }
+
// We're disabled on all engineering devices
if (Build.IS_ENG) {
Slog.v(TAG, "Disabled because of eng build");
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 9082dca..db3db0c 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -48,7 +48,7 @@
private static final long MILLIS_2010_1_1 = 1262358000000l;
- private static final String FEATURE_ID = "SensorNotificationService";
+ private static final String ATTRIBUTION_TAG = "SensorNotificationService";
private Context mContext;
private SensorManager mSensorManager;
@@ -59,7 +59,7 @@
private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
public SensorNotificationService(Context context) {
- super(context.createFeatureContext(FEATURE_ID));
+ super(context.createAttributionContext(ATTRIBUTION_TAG));
mContext = getContext();
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b7d050a..5a056d3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -154,7 +154,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.SystemService.TargetUser;
import com.android.server.pm.Installer;
import com.android.server.storage.AppFuseBridge;
import com.android.server.storage.StorageSessionController;
@@ -233,6 +232,8 @@
private static final String FUSE_ENABLED = "fuse_enabled";
private static final boolean DEFAULT_FUSE_ENABLED = true;
+ private final Set<Integer> mFuseMountedUser = new ArraySet<>();
+
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -1497,6 +1498,9 @@
@GuardedBy("mLock")
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
+ if (vol.type == VolumeInfo.TYPE_EMULATED && newState != VolumeInfo.STATE_MOUNTED) {
+ mFuseMountedUser.remove(vol.getMountUserId());
+ }
// Remember that we saw this volume so we're ready to accept user
// metadata, or so we can annoy them when a private volume is ejected
if (!TextUtils.isEmpty(vol.fsUuid)) {
@@ -2075,39 +2079,86 @@
mount(vol);
}
+ private void remountAppStorageDirs(Map<Integer, String> pidPkgMap, int userId) {
+ for (Entry<Integer, String> entry : pidPkgMap.entrySet()) {
+ final int pid = entry.getKey();
+ final String packageName = entry.getValue();
+ Slog.i(TAG, "Remounting storage for pid: " + pid);
+ final String[] sharedPackages =
+ mPmInternal.getSharedUserPackagesForPackage(packageName, userId);
+ final int uid = mPmInternal.getPackageUidInternal(packageName, 0, userId);
+ final String[] packages =
+ sharedPackages.length != 0 ? sharedPackages : new String[]{packageName};
+ try {
+ mVold.remountAppStorageDirs(uid, pid, packages);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ }
+
private void mount(VolumeInfo vol) {
try {
// TODO(b/135341433): Remove paranoid logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
- @Override
- public boolean onVolumeChecking(FileDescriptor fd, String path,
- String internalPath) {
- vol.path = path;
- vol.internalPath = internalPath;
- ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
- try {
- mStorageSessionController.onVolumeMount(pfd, vol);
- return true;
- } catch (ExternalStorageServiceException e) {
- Slog.e(TAG, "Failed to mount volume " + vol, e);
+ @Override
+ public boolean onVolumeChecking(FileDescriptor fd, String path,
+ String internalPath) {
+ vol.path = path;
+ vol.internalPath = internalPath;
+ ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
+ try {
+ mStorageSessionController.onVolumeMount(pfd, vol);
+ return true;
+ } catch (ExternalStorageServiceException e) {
+ Slog.e(TAG, "Failed to mount volume " + vol, e);
- int nextResetSeconds = REMOTE_TIMEOUT_SECONDS * 2;
- Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s");
- mHandler.removeMessages(H_RESET);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
- TimeUnit.SECONDS.toMillis(nextResetSeconds));
- return false;
- } finally {
- try {
- pfd.close();
- } catch (Exception e) {
- Slog.e(TAG, "Failed to close FUSE device fd", e);
- }
+ int nextResetSeconds = REMOTE_TIMEOUT_SECONDS * 2;
+ Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s");
+ mHandler.removeMessages(H_RESET);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
+ TimeUnit.SECONDS.toMillis(nextResetSeconds));
+ return false;
+ } finally {
+ try {
+ pfd.close();
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to close FUSE device fd", e);
}
}
- });
+ }
+ });
Slog.i(TAG, "Mounted volume " + vol);
+ if (vol.type == VolumeInfo.TYPE_EMULATED) {
+ final int userId = vol.getMountUserId();
+ mFuseMountedUser.add(userId);
+ // Async remount app storage so it won't block the main thread.
+ new Thread(() -> {
+ Map<Integer, String> pidPkgMap = null;
+ // getProcessesWithPendingBindMounts() could fail when a new app process is
+ // starting and it's not planning to mount storage dirs in zygote, but it's
+ // rare, so we retry 5 times and hope we can get the result successfully.
+ for (int i = 0; i < 5; i++) {
+ try {
+ pidPkgMap = LocalServices.getService(ActivityManagerInternal.class)
+ .getProcessesWithPendingBindMounts(vol.getMountUserId());
+ break;
+ } catch (IllegalStateException e) {
+ Slog.i(TAG, "Some processes are starting, retry");
+ // Wait 100ms and retry so hope the pending process is started.
+ SystemClock.sleep(100);
+ }
+ }
+ if (pidPkgMap != null) {
+ remountAppStorageDirs(pidPkgMap, userId);
+ } else {
+ Slog.wtf(TAG, "Not able to getStorageNotOptimizedProcesses() after"
+ + " 5 retries");
+ }
+
+ }).start();
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -4025,7 +4076,7 @@
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (mIsFuseEnabled && mMediaStoreAuthorityAppId == UserHandle.getAppId(uid)) {
+ if (mIsFuseEnabled && mStorageManagerInternal.isExternalStorageService(uid)) {
// Determine if caller requires pass_through mount; note that we do this for
// all processes that share a UID with MediaProvider; but this is fine, since
// those processes anyway share the same rights as MediaProvider.
@@ -4309,7 +4360,8 @@
pw.println();
pw.println("mObbPathToStateMap:");
pw.increaseIndent();
- final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
+ final Iterator<Entry<String, ObbState>> maps =
+ mObbPathToStateMap.entrySet().iterator();
while (maps.hasNext()) {
final Entry<String, ObbState> e = maps.next();
pw.print(e.getKey());
@@ -4350,45 +4402,43 @@
}
/**
- * Check if fuse is running in target user, if it's running then setup its obb directories.
- * TODO: System server should store a list of active pids that obb is not mounted and use it.
+ * Check if fuse is running in target user, if it's running then setup its storage dirs.
+ * Return true if storage dirs are mounted.
*/
@Override
- public void prepareObbDirs(int userId, Set<String> packageList, String processName) {
- String fuseRunningUsersList = SystemProperties.get("vold.fuse_running_users", "");
- String[] fuseRunningUsers = fuseRunningUsersList.split(",");
- boolean fuseReady = false;
- String targetUserId = String.valueOf(userId);
- for (String user : fuseRunningUsers) {
- if (targetUserId.equals(user)) {
- fuseReady = true;
- }
+ public boolean prepareStorageDirs(int userId, Set<String> packageList,
+ String processName) {
+ if (!mFuseMountedUser.contains(userId)) {
+ Slog.w(TAG, "User " + userId + " is not unlocked yet so skip mounting obb");
+ return false;
}
- if (fuseReady) {
- try {
- final IVold vold = IVold.Stub.asInterface(
- ServiceManager.getServiceOrThrow("vold"));
- for (String pkg : packageList) {
- final String packageObbDir =
- String.format("/storage/emulated/%d/Android/obb/%s/", userId, pkg);
- final String packageDataDir =
- String.format("/storage/emulated/%d/Android/data/%s/",
- userId, pkg);
+ try {
+ final IVold vold = IVold.Stub.asInterface(
+ ServiceManager.getServiceOrThrow("vold"));
+ for (String pkg : packageList) {
+ final String packageObbDir =
+ String.format("/storage/emulated/%d/Android/obb/%s/", userId, pkg);
+ final String packageDataDir =
+ String.format("/storage/emulated/%d/Android/data/%s/",
+ userId, pkg);
- // Create package obb and data dir if it doesn't exist.
- File file = new File(packageObbDir);
- if (!file.exists()) {
- vold.setupAppDir(packageObbDir, mPmInternal.getPackage(pkg).getUid());
- }
- file = new File(packageDataDir);
- if (!file.exists()) {
- vold.setupAppDir(packageDataDir, mPmInternal.getPackage(pkg).getUid());
- }
+ int appUid =
+ UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
+ // Create package obb and data dir if it doesn't exist.
+ File file = new File(packageObbDir);
+ if (!file.exists()) {
+ vold.setupAppDir(packageObbDir, appUid);
}
- } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
- Slog.e(TAG, "Unable to create obb and data directories for " + processName, e);
+ file = new File(packageDataDir);
+ if (!file.exists()) {
+ vold.setupAppDir(packageDataDir, appUid);
+ }
}
+ } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
+ Slog.e(TAG, "Unable to create obb and data directories for " + processName,e);
+ return false;
}
+ return true;
}
@Override
@@ -4476,6 +4526,11 @@
}
}
+ @Override
+ public boolean isExternalStorageService(int uid) {
+ return mMediaStoreAuthorityAppId == UserHandle.getAppId(uid);
+ }
+
public boolean hasExternalStorage(int uid, String packageName) {
// No need to check for system uid. This avoids a deadlock between
// PackageManagerService and AppOpsService.
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 35a9802..f772a4a 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -53,6 +53,7 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -230,6 +231,7 @@
@Nullable LinkProperties lp,
boolean isMetered,
int callingUid,
+ @NonNull int[] administratorUids,
@NonNull IBinder binder)
throws RemoteException, SocketException {
Objects.requireNonNull(looper, "missing Looper");
@@ -248,6 +250,7 @@
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
+ nc.setAdministratorUids(intArrayToList(administratorUids));
if (!isMetered) {
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
}
@@ -290,6 +293,14 @@
return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder);
}
+ private List<Integer> intArrayToList(@NonNull int[] array) {
+ final List<Integer> list = new ArrayList<>(array.length);
+ for (final int i : array) {
+ list.add(i);
+ }
+ return list;
+ }
+
/**
* Sets up a Network with extremely limited privileges, guarded by the MANAGE_TEST_NETWORKS
* permission.
@@ -301,6 +312,7 @@
@NonNull String iface,
@Nullable LinkProperties lp,
boolean isMetered,
+ @NonNull int[] administratorUids,
@NonNull IBinder binder) {
enforceTestNetworkPermissions(mContext);
@@ -335,6 +347,7 @@
lp,
isMetered,
callingUid,
+ administratorUids,
binder);
mTestNetworkTracker.put(agent.getNetwork().netId, agent);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 7c833fa..12a1a95 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -75,7 +75,9 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -293,7 +295,7 @@
public void onChange(boolean selfChange, Uri uri) {
synchronized (mLock) {
// setup wizard is done now so we can unblock
- if (setupWizardCompleteForCurrentUser()) {
+ if (setupWizardCompleteForCurrentUser() && !selfChange) {
mSetupWizardComplete = true;
getContext().getContentResolver()
.unregisterContentObserver(mSetupWizardObserver);
@@ -348,6 +350,9 @@
IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(mBatteryReceiver, batteryFilter);
+ context.registerReceiver(mSettingsRestored,
+ new IntentFilter(Intent.ACTION_SETTING_RESTORED), null, mHandler);
+
mLocalPowerManager =
LocalServices.getService(PowerManagerInternal.class);
initPowerSave();
@@ -395,6 +400,22 @@
mHandler.post(() -> updateSystemProperties());
}
+ private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ List<String> settings = Arrays.asList(
+ Secure.UI_NIGHT_MODE, Secure.DARK_THEME_CUSTOM_START_TIME,
+ Secure.DARK_THEME_CUSTOM_END_TIME);
+ if (settings.contains(intent.getExtras().getCharSequence(Intent.EXTRA_SETTING_NAME))) {
+ synchronized (mLock) {
+ updateNightModeFromSettingsLocked(context, context.getResources(),
+ UserHandle.getCallingUserId());
+ updateConfigurationLocked();
+ }
+ }
+ }
+ };
+
private void initPowerSave() {
mPowerSave =
mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE)
@@ -1297,9 +1318,9 @@
if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
try {
int result = ActivityTaskManager.getService().startActivityWithConfig(
- null, getContext().getBasePackageName(), getContext().getFeatureId(),
- homeIntent, null, null, null, 0, 0, mConfiguration, null,
- UserHandle.USER_CURRENT);
+ null, getContext().getBasePackageName(),
+ getContext().getAttributionTag(), homeIntent, null, null, null, 0, 0,
+ mConfiguration, null, UserHandle.USER_CURRENT);
if (ActivityManager.isStartResultSuccessful(result)) {
dockAppStarted = true;
} else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index cfc8b45..0ebb5bb 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -137,6 +137,10 @@
private static final boolean SHOW_DUNGEON_NOTIFICATION = false;
+ //TODO: remove this when development is done.
+ private static final int DEBUG_FGS_ALLOW_WHILE_IN_USE = 0;
+ private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
+
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
@@ -1701,7 +1705,7 @@
if (acceptances > 0 || rejections > 0) {
FrameworkStatsLog.write(
FrameworkStatsLog.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED,
- mProcessRecord.uid, opToEnum(op),
+ mProcessRecord.uid, AppOpsManager.opToLoggingId(op),
modeToEnum(mAppOpModes.get(op)),
acceptances, rejections
);
@@ -1725,22 +1729,6 @@
}
}
- /** Maps AppOp op value to atoms.proto enum. */
- private static int opToEnum(int op) {
- switch (op) {
- case AppOpsManager.OP_COARSE_LOCATION: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_COARSE_LOCATION;
- case AppOpsManager.OP_FINE_LOCATION: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_FINE_LOCATION;
- case AppOpsManager.OP_CAMERA: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_CAMERA;
- case AppOpsManager.OP_RECORD_AUDIO: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_RECORD_AUDIO;
- default: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_NONE;
- }
- }
-
private void cancelForegroundNotificationLocked(ServiceRecord r) {
if (r.foregroundId != 0) {
// First check to see if this app has any other active foreground services
@@ -4931,10 +4919,20 @@
if (!r.isForeground) {
continue;
}
- if (!r.mAllowWhileInUsePermissionInFgs
- && r.mInfoDenyWhileInUsePermissionInFgs != null) {
- final String msg = r.mInfoDenyWhileInUsePermissionInFgs
- + " affected while-in-use permission:"
+ if (mode == DEBUG_FGS_ALLOW_WHILE_IN_USE) {
+ if (!r.mAllowWhileInUsePermissionInFgs
+ && r.mInfoDenyWhileInUsePermissionInFgs != null) {
+ final String msg = r.mInfoDenyWhileInUsePermissionInFgs
+ + " affected while-in-use permission:"
+ + AppOpsManager.opToPublicName(op);
+ Slog.wtf(TAG, msg);
+ }
+ } else if (mode == DEBUG_FGS_ENFORCE_TYPE) {
+ final String msg =
+ "FGS Missing foregroundServiceType in manifest file [callingPackage: "
+ + r.mRecentCallingPackage
+ + "; intent:" + r.intent.getIntent()
+ + "] affected while-in-use permission:"
+ AppOpsManager.opToPublicName(op);
Slog.wtf(TAG, msg);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f64272b..ebca1f7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2179,9 +2179,17 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(false);
+ }
+
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
+
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(true);
+ }
}
}
@@ -2193,9 +2201,17 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(false);
+ }
+
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"gfxinfo", pw)) return;
mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
+
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(true);
+ }
}
}
@@ -2207,9 +2223,17 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(false);
+ }
+
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"dbinfo", pw)) return;
mActivityManagerService.dumpDbInfo(fd, pw, args);
+
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(true);
+ }
}
}
@@ -17538,8 +17562,11 @@
final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
if (proc.thread != null && proc.baseProcessTracker != null) {
- proc.baseProcessTracker.setState(
- proc.getReportedProcState(), memFactor, now, proc.pkgList.mPkgList);
+ final int procState = proc.getReportedProcState();
+ if (procState != PROCESS_STATE_NONEXISTENT) {
+ proc.baseProcessTracker.setState(
+ procState, memFactor, now, proc.pkgList.mPkgList);
+ }
}
}
@@ -18714,6 +18741,11 @@
}
@Override
+ public Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
+ return mProcessList.getProcessesWithPendingBindMounts(userId);
+ }
+
+ @Override
public boolean isSystemReady() {
// no need to synchronize(this) just to read & return the value
return mSystemReady;
@@ -19810,6 +19842,7 @@
void setPermissions(@Nullable String[] permissions) {
mPermissions = permissions;
+ PackageManager.invalidatePackageInfoCache();
}
@Override
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index ba89fce..03f4a54 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -16,15 +16,20 @@
package com.android.server.am;
+import static android.app.AppOpsManager.OP_NONE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.app.Activity;
import android.app.BroadcastOptions;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.BugreportManager;
+import android.os.BugreportParams;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -110,9 +115,17 @@
options.setBackgroundActivityStartsAllowed(true);
final long identity = Binder.clearCallingIdentity();
try {
- context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
+ // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
+ // let ResultBroadcastReceiver know the handler app is available.
+ context.sendOrderedBroadcastAsUser(intent,
+ UserHandle.of(handlerUser),
android.Manifest.permission.DUMP,
- options.toBundle());
+ OP_NONE, options.toBundle(),
+ new ResultBroadcastReceiver(),
+ /* scheduler= */ null,
+ Activity.RESULT_CANCELED,
+ /* initialData= */ null,
+ /* initialExtras= */ null);
} catch (RuntimeException e) {
Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
return false;
@@ -176,4 +189,19 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ private static class ResultBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() == Activity.RESULT_OK) {
+ return;
+ }
+
+ Slog.w(TAG, "Request bug report because handler app seems to be not available.");
+ BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
+ bugreportManager.requestBugreport(
+ new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
+ /* shareTitle= */null, /* shareDescription= */ null);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 48ceba9..8527ae9 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -115,6 +115,10 @@
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE,
WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class,
WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE,
+ WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class,
+ WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT));
sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES,
WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, boolean.class,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index b584ea5..2b1534b 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -75,7 +75,6 @@
import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.content.pm.ServiceInfo;
@@ -149,12 +148,13 @@
* capabilities.
*/
@ChangeId
- //TODO: change to @EnabledAfter when enforcing the feature.
- @Disabled
+ @EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
static final long CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID = 136219221L;
//TODO: remove this when development is done.
private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA = 1 << 30;
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 29;
/**
* For some direct access we need to power manager.
@@ -1513,10 +1513,12 @@
if (enabled) {
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_CAMERA)
- != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA : 0;
+ != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA
+ : TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA;
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_MICROPHONE)
- != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE : 0;
+ != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
+ : TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
} else {
capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
| PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 6b16513..16a7c6b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -61,6 +61,7 @@
import android.app.IApplicationThread;
import android.app.IUidObserver;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -70,6 +71,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ProcessInfo;
import android.content.res.Resources;
import android.graphics.Point;
import android.net.LocalSocket;
@@ -97,6 +99,7 @@
import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -134,8 +137,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Activity manager code dealing with processes.
@@ -152,7 +158,8 @@
"persist.sys.vold_app_data_isolation_enabled";
// A device config to control the minimum target SDK to enable app data isolation
- static final String ANDROID_APP_DATA_ISOLATION_MIN_SDK = "android_app_data_isolation_min_sdk";
+ static final String ANDROID_APP_DATA_ISOLATION_MIN_SDK =
+ "android_app_data_isolation_min_sdk";
// The minimum time we allow between crashes, for us to consider this
// application to be bad and stop and its services and reject broadcasts.
@@ -345,6 +352,14 @@
private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
/**
+ * Enable sampled memory bug detection in the app.
+ * @see <a href="https://source.android.com/devices/tech/debug/gwp-asan">GWP-ASan</a>.
+ */
+ @ChangeId
+ @Disabled
+ private static final long GWP_ASAN = 135634846; // This is a bug id.
+
+ /**
* Apps have no access to the private data directories of any other app, even if the other
* app has made them world-readable.
*/
@@ -789,6 +804,31 @@
}
}
+ /**
+ * Get a map of pid and package name that process of that pid Android/data and Android/obb
+ * directory is not mounted to lowerfs to speed up access.
+ */
+ Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
+ final Map<Integer, String> pidPackageMap = new HashMap<>();
+ synchronized (mService) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord record = mLruProcesses.get(i);
+ if (record.userId != userId || !record.bindMountPending) {
+ continue;
+ }
+ final int pid = record.pid;
+ // It can happen when app process is starting, but zygote work is not done yet so
+ // system does not this pid record yet.
+ if (pid == 0) {
+ throw new IllegalStateException("Pending process is not started yet,"
+ + "retry later");
+ }
+ pidPackageMap.put(pid, record.info.packageName);
+ }
+ return pidPackageMap;
+ }
+ }
+
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
// Scale buckets from avail memory: at 300MB we use the lowest values to
// 700MB or more for the top values.
@@ -1634,6 +1674,28 @@
return gidArray;
}
+ private int decideGwpAsanLevel(ProcessRecord app) {
+ // Look at the process attribute first.
+ if (app.processInfo != null && app.processInfo.enableGwpAsan != null) {
+ return app.processInfo.enableGwpAsan ? Zygote.GWP_ASAN_LEVEL_ALWAYS
+ : Zygote.GWP_ASAN_LEVEL_NEVER;
+ }
+ // Then at the applicaton attribute.
+ if (app.info.isGwpAsanEnabled() != null) {
+ return app.info.isGwpAsanEnabled() ? Zygote.GWP_ASAN_LEVEL_ALWAYS
+ : Zygote.GWP_ASAN_LEVEL_NEVER;
+ }
+ // If the app does not specify enableGwpAsan, the default behavior is lottery among the
+ // system apps, and disabled for user apps, unless overwritten by the compat feature.
+ if (mPlatformCompat.isChangeEnabled(GWP_ASAN, app.info)) {
+ return Zygote.GWP_ASAN_LEVEL_ALWAYS;
+ }
+ if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return Zygote.GWP_ASAN_LEVEL_LOTTERY;
+ }
+ return Zygote.GWP_ASAN_LEVEL_NEVER;
+ }
+
/**
* @return {@code true} if process start is successful, false otherwise.
*/
@@ -1648,6 +1710,7 @@
if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
checkSlow(startTime, "startProcess: removing from pids map");
mService.removePidLocked(app);
+ app.bindMountPending = false;
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
checkSlow(startTime, "startProcess: done removing from pids map");
app.setPid(0);
@@ -1803,6 +1866,8 @@
runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
}
+ runtimeFlags |= decideGwpAsanLevel(app);
+
String invokeWith = null;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// Debuggable apps may include a wrapper script with their library directory.
@@ -2127,7 +2192,10 @@
app.setHasForegroundActivities(true);
}
+ StorageManagerInternal storageManagerInternal = LocalServices.getService(
+ StorageManagerInternal.class);
final Map<String, Pair<String, Long>> pkgDataInfoMap;
+ boolean bindMountAppStorageDirs = false;
if (shouldIsolateAppData(app)) {
// Get all packages belongs to the same shared uid. sharedPackages is empty array
@@ -2138,11 +2206,17 @@
pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
? new String[]{app.info.packageName} : sharedPackages, uid);
- if (mVoldAppDataIsolationEnabled) {
- StorageManagerInternal storageManagerInternal = LocalServices.getService(
- StorageManagerInternal.class);
- storageManagerInternal.prepareObbDirs(UserHandle.getUserId(uid),
- pkgDataInfoMap.keySet(), app.processName);
+ int userId = UserHandle.getUserId(uid);
+ if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) &&
+ !storageManagerInternal.isExternalStorageService(uid)) {
+ bindMountAppStorageDirs = true;
+ if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+ app.processName)) {
+ // Cannot prepare Android/app and Android/obb directory,
+ // so we won't mount it in zygote.
+ app.bindMountPending = true;
+ bindMountAppStorageDirs = false;
+ }
}
} else {
pkgDataInfoMap = null;
@@ -2163,7 +2237,7 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
- app.mDisabledCompatChanges, pkgDataInfoMap,
+ app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
@@ -2171,6 +2245,7 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
+ bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
checkSlow(startTime, "startProcess: returned from zygote!");
@@ -2629,6 +2704,7 @@
int pid = app.pid;
if (pid > 0) {
mService.removePidLocked(app);
+ app.bindMountPending = false;
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c029811..e7f66bb 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -251,6 +251,7 @@
int adjSourceProcState; // Debugging: proc state of adjSource's process.
Object adjTarget; // Debugging: target component impacting oom_adj.
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
+ boolean bindMountPending; // True if Android/obb and Android/data need to be bind mount .
// Cache of last retrieve memory info and uptime, to throttle how frequently
// apps can requyest it.
@@ -384,6 +385,9 @@
pw.println(processInfo.deniedPermissions.valueAt(i));
}
}
+ if (processInfo.enableGwpAsan != null) {
+ pw.print(prefix); pw.println(" enableGwpAsan=" + processInfo.enableGwpAsan);
+ }
}
pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi);
pw.print(" instructionSet="); pw.println(instructionSet);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 0a8e70c..bac7565 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -80,6 +80,7 @@
@VisibleForTesting
static final String[] sDeviceConfigScopes = new String[] {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_CONFIGURATION,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a7125b4..343b4ba 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2218,19 +2218,23 @@
/**
* Returns whether the given user requires credential entry at this time. This is used to
- * intercept activity launches for work apps when the Work Challenge is present.
+ * intercept activity launches for locked work apps due to work challenge being triggered
+ * or when the profile user is yet to be unlocked.
*/
protected boolean shouldConfirmCredentials(@UserIdInt int userId) {
- synchronized (mLock) {
- if (mStartedUsers.get(userId) == null) {
- return false;
- }
- }
- if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ if (getStartedUserState(userId) == null) {
return false;
}
- final KeyguardManager km = mInjector.getKeyguardManager();
- return km.isDeviceLocked(userId) && km.isDeviceSecure(userId);
+ if (!getUserInfo(userId).isManagedProfile()) {
+ return false;
+ }
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ final KeyguardManager km = mInjector.getKeyguardManager();
+ return km.isDeviceLocked(userId) && km.isDeviceSecure(userId);
+ } else {
+ // For unified challenge, need to confirm credential if user is RUNNING_LOCKED.
+ return isUserRunning(userId, ActivityManager.FLAG_AND_LOCKED);
+ }
}
boolean isLockScreenDisabled(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 471c97b..2e2241f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,8 +19,8 @@
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
-import static android.app.AppOpsManager.CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE;
-import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
+import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
+import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
@@ -41,6 +41,7 @@
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OpEventProxyInfo;
import static android.app.AppOpsManager.RestrictionBypass;
+import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
@@ -77,17 +78,16 @@
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AppOpsManager.AttributedOpEntry;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.Mode;
import android.app.AppOpsManager.OpEntry;
-import android.app.AppOpsManager.OpFeatureEntry;
import android.app.AppOpsManager.OpFlags;
import android.app.AppOpsManagerInternal;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.app.AsyncNotedAppOp;
import android.app.RuntimeAppOpAccessMessage;
import android.app.SyncNotedAppOp;
-import android.compat.Compatibility;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -98,7 +98,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
-import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedAttribution;
import android.database.ContentObserver;
import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
import android.net.Uri;
@@ -245,9 +245,15 @@
private static final int MAX_UNFORWARED_OPS = 10;
private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
+ private static final int RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS = 300000;
//TODO: remove this when development is done.
private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA = 1 << 30;
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 29;
+ private static final int DEBUG_FGS_ALLOW_WHILE_IN_USE = 0;
+ private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
+
final Context mContext;
final AtomicFile mFile;
@@ -372,14 +378,14 @@
}
OpEventProxyInfo acquire(@IntRange(from = 0) int uid, @Nullable String packageName,
- @Nullable String featureId) {
+ @Nullable String attributionTag) {
OpEventProxyInfo recycled = acquire();
if (recycled != null) {
- recycled.reinit(uid, packageName, featureId);
+ recycled.reinit(uid, packageName, attributionTag);
return recycled;
}
- return new OpEventProxyInfo(uid, packageName, featureId);
+ return new OpEventProxyInfo(uid, packageName, attributionTag);
}
}
@@ -550,7 +556,7 @@
// The FGS has the location capability, but due to FGS BG start
// restriction it lost the capability, use temp location capability
// to mark this case.
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
} else {
return MODE_IGNORED;
@@ -558,15 +564,25 @@
case OP_CAMERA:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
return MODE_ALLOWED;
+ } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA)
+ != 0) {
+ // CHANGE TO MODE_IGNORED when enforce this feature.
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+ return MODE_ALLOWED;
} else {
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
}
case OP_RECORD_AUDIO:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
return MODE_ALLOWED;
+ } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE)
+ != 0) {
+ // CHANGE TO MODE_IGNORED when enforce this feature.
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+ return MODE_ALLOWED;
} else {
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
}
default:
@@ -581,15 +597,24 @@
case OP_CAMERA:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
return MODE_ALLOWED;
+ } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
+ // CHANGE TO MODE_IGNORED when enforce this feature.
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+ return MODE_ALLOWED;
} else {
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
}
case OP_RECORD_AUDIO:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
return MODE_ALLOWED;
+ } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE)
+ != 0) {
+ // CHANGE TO MODE_IGNORED when enforce this feature.
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+ return MODE_ALLOWED;
} else {
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
}
default:
@@ -672,8 +697,8 @@
*/
@Nullable RestrictionBypass bypass;
- /** Lazily populated cache of featureIds of this package */
- final @NonNull ArraySet<String> knownFeatureIds = new ArraySet<>();
+ /** Lazily populated cache of attributionTags of this package */
+ final @NonNull ArraySet<String> knownAttributionTags = new ArraySet<>();
Ops(String _packageName, UidState _uidState) {
packageName = _packageName;
@@ -777,8 +802,8 @@
}
}
- private final class FeatureOp {
- public final @Nullable String featureId;
+ private final class AttributedOp {
+ public final @Nullable String tag;
public final @NonNull Op parent;
/**
@@ -805,8 +830,8 @@
@GuardedBy("AppOpsService.this")
private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
- FeatureOp(@Nullable String featureId, @NonNull Op parent) {
- this.featureId = featureId;
+ AttributedOp(@Nullable String tag, @NonNull Op parent) {
+ this.tag = tag;
this.parent = parent;
}
@@ -815,18 +840,18 @@
*
* @param proxyUid The uid of the proxy
* @param proxyPackageName The package name of the proxy
- * @param proxyFeatureId the featureId in the proxies package
+ * @param proxyAttributionTag the attributionTag in the proxies package
* @param uidState UID state of the app noteOp/startOp was called for
* @param flags OpFlags of the call
*/
public void accessed(int proxyUid, @Nullable String proxyPackageName,
- @Nullable String proxyFeatureId, @AppOpsManager.UidState int uidState,
+ @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState,
@OpFlags int flags) {
accessed(System.currentTimeMillis(), -1, proxyUid, proxyPackageName,
- proxyFeatureId, uidState, flags);
+ proxyAttributionTag, uidState, flags);
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- featureId, uidState, flags);
+ tag, uidState, flags);
}
/**
@@ -836,12 +861,12 @@
* @param duration The duration of the event
* @param proxyUid The uid of the proxy
* @param proxyPackageName The package name of the proxy
- * @param proxyFeatureId the featureId in the proxies package
+ * @param proxyAttributionTag the attributionTag in the proxies package
* @param uidState UID state of the app noteOp/startOp was called for
* @param flags OpFlags of the call
*/
public void accessed(long noteTime, long duration, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyFeatureId,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
@AppOpsManager.UidState int uidState, @OpFlags int flags) {
long key = makeKey(uidState, flags);
@@ -852,7 +877,7 @@
OpEventProxyInfo proxyInfo = null;
if (proxyUid != Process.INVALID_UID) {
proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
- proxyFeatureId);
+ proxyAttributionTag);
}
NoteOpEvent existingEvent = mAccessEvents.get(key);
@@ -873,7 +898,7 @@
rejected(System.currentTimeMillis(), uidState, flags);
mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, parent.packageName,
- featureId, uidState, flags);
+ tag, uidState, flags);
}
/**
@@ -939,7 +964,7 @@
// startOp events don't support proxy, hence use flags==SELF
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- featureId, uidState, OP_FLAG_SELF);
+ tag, uidState, OP_FLAG_SELF);
}
/**
@@ -979,7 +1004,7 @@
mAccessEvents.put(makeKey(event.getUidState(), OP_FLAG_SELF), finishedEvent);
mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
- parent.packageName, featureId, event.getUidState(),
+ parent.packageName, tag, event.getUidState(),
AppOpsManager.OP_FLAG_SELF, finishedEvent.getDuration());
mInProgressStartOpEventPool.release(event);
@@ -987,7 +1012,7 @@
if (mInProgressEvents.isEmpty()) {
mInProgressEvents = null;
- // TODO moltmann: Also callback for single feature activity changes
+ // TODO moltmann: Also callback for single attribution tag activity changes
if (triggerCallbackIfNeeded && !parent.isRunning()) {
scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, false);
@@ -1078,14 +1103,14 @@
}
/**
- * Add all data from the {@code featureToAdd} to this op.
+ * Add all data from the {@code opToAdd} to this op.
*
* <p>If there is an event for the same key in both the later event is retained.
* <p>{@code opToAdd} should not be used after this method is called.
*
* @param opToAdd The op to add
*/
- public void add(@NonNull FeatureOp opToAdd) {
+ public void add(@NonNull AttributedOp opToAdd) {
if (opToAdd.mInProgressEvents != null) {
Slog.w(TAG, "Ignoring " + opToAdd.mInProgressEvents.size() + " running app-ops");
@@ -1129,7 +1154,7 @@
return clone;
}
- @NonNull OpFeatureEntry createFeatureEntryLocked() {
+ @NonNull AttributedOpEntry createAttributedOpEntryLocked() {
LongSparseArray<NoteOpEvent> accessEvents = deepClone(mAccessEvents);
// Add in progress events as access events
@@ -1153,7 +1178,7 @@
LongSparseArray<NoteOpEvent> rejectEvents = deepClone(mRejectEvents);
- return new OpFeatureEntry(parent.op, isRunning(), accessEvents, rejectEvents);
+ return new AttributedOpEntry(parent.op, isRunning(), accessEvents, rejectEvents);
}
}
@@ -1165,8 +1190,8 @@
private @Mode int mode;
- /** featureId -> FeatureOp */
- final ArrayMap<String, FeatureOp> mFeatures = new ArrayMap<>(1);
+ /** attributionTag -> AttributedOp */
+ final ArrayMap<String, AttributedOp> mAttributions = new ArrayMap<>(1);
Op(UidState uidState, String packageName, int op, int uid) {
this.op = op;
@@ -1184,58 +1209,59 @@
return uidState.evalMode(op, mode);
}
- void removeFeaturesWithNoTime() {
- for (int i = mFeatures.size() - 1; i >= 0; i--) {
- if (!mFeatures.valueAt(i).hasAnyTime()) {
- mFeatures.removeAt(i);
+ void removeAttributionsWithNoTime() {
+ for (int i = mAttributions.size() - 1; i >= 0; i--) {
+ if (!mAttributions.valueAt(i).hasAnyTime()) {
+ mAttributions.removeAt(i);
}
}
}
- private @NonNull FeatureOp getOrCreateFeature(@NonNull Op parent,
- @Nullable String featureId) {
- FeatureOp featureOp;
+ private @NonNull AttributedOp getOrCreateAttribution(@NonNull Op parent,
+ @Nullable String attributionTag) {
+ AttributedOp attributedOp;
- featureOp = mFeatures.get(featureId);
- if (featureOp == null) {
- featureOp = new FeatureOp(featureId, parent);
- mFeatures.put(featureId, featureOp);
+ attributedOp = mAttributions.get(attributionTag);
+ if (attributedOp == null) {
+ attributedOp = new AttributedOp(attributionTag, parent);
+ mAttributions.put(attributionTag, attributedOp);
}
- return featureOp;
+ return attributedOp;
}
@NonNull OpEntry createEntryLocked() {
- final int numFeatures = mFeatures.size();
+ final int numAttributions = mAttributions.size();
- final ArrayMap<String, OpFeatureEntry> featureEntries = new ArrayMap<>(numFeatures);
- for (int i = 0; i < numFeatures; i++) {
- featureEntries.put(mFeatures.keyAt(i),
- mFeatures.valueAt(i).createFeatureEntryLocked());
+ final ArrayMap<String, AppOpsManager.AttributedOpEntry> attributionEntries =
+ new ArrayMap<>(numAttributions);
+ for (int i = 0; i < numAttributions; i++) {
+ attributionEntries.put(mAttributions.keyAt(i),
+ mAttributions.valueAt(i).createAttributedOpEntryLocked());
}
- return new OpEntry(op, mode, featureEntries);
+ return new OpEntry(op, mode, attributionEntries);
}
- @NonNull OpEntry createSingleFeatureEntryLocked(@Nullable String featureId) {
- final int numFeatures = mFeatures.size();
+ @NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
+ final int numAttributions = mAttributions.size();
- final ArrayMap<String, OpFeatureEntry> featureEntries = new ArrayMap<>(1);
- for (int i = 0; i < numFeatures; i++) {
- if (Objects.equals(mFeatures.keyAt(i), featureId)) {
- featureEntries.put(mFeatures.keyAt(i),
- mFeatures.valueAt(i).createFeatureEntryLocked());
+ final ArrayMap<String, AttributedOpEntry> attributionEntries = new ArrayMap<>(1);
+ for (int i = 0; i < numAttributions; i++) {
+ if (Objects.equals(mAttributions.keyAt(i), attributionTag)) {
+ attributionEntries.put(mAttributions.keyAt(i),
+ mAttributions.valueAt(i).createAttributedOpEntryLocked());
break;
}
}
- return new OpEntry(op, mode, featureEntries);
+ return new OpEntry(op, mode, attributionEntries);
}
boolean isRunning() {
- final int numFeatures = mFeatures.size();
- for (int i = 0; i < numFeatures; i++) {
- if (mFeatures.valueAt(i).isRunning()) {
+ final int numAttributions = mAttributions.size();
+ for (int i = 0; i < numAttributions; i++) {
+ if (mAttributions.valueAt(i).isRunning()) {
return true;
}
}
@@ -1408,10 +1434,11 @@
}
/**
- * Call {@link FeatureOp#onClientDeath featureOp.onClientDeath(clientId)}.
+ * Call {@link AttributedOp#onClientDeath attributedOp.onClientDeath(clientId)}.
*/
- private static void onClientDeath(@NonNull FeatureOp featureOp, @NonNull IBinder clientId) {
- featureOp.onClientDeath(clientId);
+ private static void onClientDeath(@NonNull AttributedOp attributedOp,
+ @NonNull IBinder clientId) {
+ attributedOp.onClientDeath(clientId);
}
@@ -1493,20 +1520,21 @@
return;
}
- ArrayMap<String, String> dstFeatureIds = new ArrayMap<>();
- ArraySet<String> featureIds = new ArraySet<>();
- featureIds.add(null);
- if (pkg.getFeatures() != null) {
- int numFeatures = pkg.getFeatures().size();
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- ParsedFeature feature = pkg.getFeatures().get(featureNum);
- featureIds.add(feature.id);
+ ArrayMap<String, String> dstAttributionTags = new ArrayMap<>();
+ ArraySet<String> attributionTags = new ArraySet<>();
+ attributionTags.add(null);
+ if (pkg.getAttributions() != null) {
+ int numAttributions = pkg.getAttributions().size();
+ for (int attributionNum = 0; attributionNum < numAttributions;
+ attributionNum++) {
+ ParsedAttribution attribution = pkg.getAttributions().get(attributionNum);
+ attributionTags.add(attribution.tag);
- int numInheritFrom = feature.inheritFrom.size();
+ int numInheritFrom = attribution.inheritFrom.size();
for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
inheritFromNum++) {
- dstFeatureIds.put(feature.inheritFrom.get(inheritFromNum),
- feature.id);
+ dstAttributionTags.put(attribution.inheritFrom.get(inheritFromNum),
+ attribution.tag);
}
}
}
@@ -1524,27 +1552,30 @@
// Reset cached package properties to re-initialize when needed
ops.bypass = null;
- ops.knownFeatureIds.clear();
+ ops.knownAttributionTags.clear();
- // Merge data collected for removed features into their successor features
+ // Merge data collected for removed attributions into their successor
+ // attributions
int numOps = ops.size();
for (int opNum = 0; opNum < numOps; opNum++) {
Op op = ops.valueAt(opNum);
- int numFeatures = op.mFeatures.size();
- for (int featureNum = numFeatures - 1; featureNum >= 0; featureNum--) {
- String featureId = op.mFeatures.keyAt(featureNum);
+ int numAttributions = op.mAttributions.size();
+ for (int attributionNum = numAttributions - 1; attributionNum >= 0;
+ attributionNum--) {
+ String attributionTag = op.mAttributions.keyAt(attributionNum);
- if (featureIds.contains(featureId)) {
- // feature still exist after upgrade
+ if (attributionTags.contains(attributionTag)) {
+ // attribution still exist after upgrade
continue;
}
- String newFeatureId = dstFeatureIds.get(featureId);
+ String newAttributionTag = dstAttributionTags.get(attributionTag);
- FeatureOp newFeatureOp = op.getOrCreateFeature(op, newFeatureId);
- newFeatureOp.add(op.mFeatures.valueAt(featureNum));
- op.mFeatures.removeAt(featureNum);
+ AttributedOp newAttributedOp = op.getOrCreateAttribution(op,
+ newAttributionTag);
+ newAttributedOp.add(op.mAttributions.valueAt(attributionNum));
+ op.mAttributions.removeAt(attributionNum);
scheduleFastWriteLocked();
}
@@ -1652,17 +1683,14 @@
}
}, packageAddedFilter);
- List<String> packageNames = getPackageNamesForSampling();
- synchronized (this) {
- resamplePackageAndAppOpLocked(packageNames);
- }
-
- AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+ mHandler.postDelayed(new Runnable() {
@Override
public void run() {
+ List<String> packageNames = getPackageNamesForSampling();
+ resamplePackageAndAppOpLocked(packageNames);
initializeRarelyUsedPackagesList(new ArraySet<>(packageNames));
}
- });
+ }, RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS);
PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
@@ -1744,12 +1772,13 @@
for (int opNum = 0; opNum < numOps; opNum++) {
final Op op = ops.valueAt(opNum);
- final int numFeatures = op.mFeatures.size();
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- FeatureOp featureOp = op.mFeatures.valueAt(featureNum);
+ final int numAttributions = op.mAttributions.size();
+ for (int attributionNum = 0; attributionNum < numAttributions;
+ attributionNum++) {
+ AttributedOp attributedOp = op.mAttributions.valueAt(attributionNum);
- while (featureOp.mInProgressEvents != null) {
- featureOp.finished(featureOp.mInProgressEvents.keyAt(0));
+ while (attributedOp.mInProgressEvents != null) {
+ attributedOp.finished(attributedOp.mInProgressEvents.keyAt(0));
}
}
}
@@ -1829,11 +1858,13 @@
for (int opNum = 0; opNum < numOps; opNum++) {
Op op = ops.valueAt(opNum);
- int numFeatures = op.mFeatures.size();
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- FeatureOp featureOp = op.mFeatures.valueAt(featureNum);
+ int numAttributions = op.mAttributions.size();
+ for (int attributionNum = 0; attributionNum < numAttributions;
+ attributionNum++) {
+ AttributedOp attributedOp = op.mAttributions.valueAt(
+ attributionNum);
- featureOp.onUidStateChanged(newState);
+ attributedOp.onUidStateChanged(newState);
}
}
}
@@ -1979,9 +2010,9 @@
/**
* Verify that historical appop request arguments are valid.
*/
- private void ensureHistoricalOpRequestIsValid(int uid, String packageName, String featureId,
- List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
- int flags) {
+ private void ensureHistoricalOpRequestIsValid(int uid, String packageName,
+ String attributionTag, List<String> opNames, int filter, long beginTimeMillis,
+ long endTimeMillis, int flags) {
if ((filter & FILTER_BY_UID) != 0) {
Preconditions.checkArgument(uid != Process.INVALID_UID);
} else {
@@ -1994,8 +2025,8 @@
Preconditions.checkArgument(packageName == null);
}
- if ((filter & FILTER_BY_FEATURE_ID) == 0) {
- Preconditions.checkArgument(featureId == null);
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) == 0) {
+ Preconditions.checkArgument(attributionTag == null);
}
if ((filter & FILTER_BY_OP_NAMES) != 0) {
@@ -2005,17 +2036,18 @@
}
Preconditions.checkFlagsArgument(filter,
- FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_FEATURE_ID | FILTER_BY_OP_NAMES);
+ FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_ATTRIBUTION_TAG
+ | FILTER_BY_OP_NAMES);
Preconditions.checkArgumentNonnegative(beginTimeMillis);
Preconditions.checkArgument(endTimeMillis > beginTimeMillis);
Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL);
}
@Override
- public void getHistoricalOps(int uid, String packageName, String featureId,
+ public void getHistoricalOps(int uid, String packageName, String attributionTag,
List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
int flags, RemoteCallback callback) {
- ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter,
+ ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
@@ -2036,15 +2068,15 @@
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
- mHistoricalRegistry, uid, packageName, featureId, opNamesArray, filter,
+ mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, filter,
beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
}
@Override
- public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId,
+ public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String attributionTag,
List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
int flags, RemoteCallback callback) {
- ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter,
+ ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
beginTimeMillis, endTimeMillis, flags);
Objects.requireNonNull(callback, "callback cannot be null");
@@ -2056,7 +2088,7 @@
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
- mHistoricalRegistry, uid, packageName, featureId, opNamesArray,
+ mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray,
filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
}
@@ -2090,9 +2122,9 @@
}
private void pruneOpLocked(Op op, int uid, String packageName) {
- op.removeFeaturesWithNoTime();
+ op.removeAttributionsWithNoTime();
- if (op.mFeatures.size() == 0) {
+ if (op.mAttributions.isEmpty()) {
Ops ops = getOpsLocked(uid, packageName, null, null, false /* edit */);
if (ops != null) {
ops.remove(op.op);
@@ -2611,8 +2643,8 @@
callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
mPackageModeWatchers.get(packageName));
- curOp.removeFeaturesWithNoTime();
- if (curOp.mFeatures.size() == 0) {
+ curOp.removeAttributionsWithNoTime();
+ if (curOp.mAttributions.isEmpty()) {
pkgOps.removeAt(j);
}
}
@@ -2679,10 +2711,8 @@
synchronized (this) {
int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
- // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
int notifiedOps;
- if (Compatibility.isChangeEnabled(
- CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) {
+ if ((flags & CALL_BACK_ON_SWITCHED_OP) == 0) {
if (op == OP_NONE) {
notifiedOps = ALL_OPS;
} else {
@@ -2898,8 +2928,8 @@
@Override
public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
- String proxiedFeatureId, int proxyUid, String proxyPackageName,
- String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message) {
+ String proxiedAttributionTag, int proxyUid, String proxyPackageName,
+ String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message) {
verifyIncomingUid(proxyUid);
verifyIncomingOp(code);
@@ -2915,7 +2945,7 @@
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName,
- proxyFeatureId, Process.INVALID_UID, null, null, proxyFlags,
+ proxyAttributionTag, Process.INVALID_UID, null, null, proxyFlags,
!isProxyTrusted, "proxy " + message);
if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
return proxyMode;
@@ -2928,27 +2958,27 @@
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
- proxiedFeatureId, proxyUid, resolveProxyPackageName, proxyFeatureId,
+ proxiedAttributionTag, proxyUid, resolveProxyPackageName, proxyAttributionTag,
proxiedFlags, shouldCollectAsyncNotedOp, message);
}
@Override
- public int noteOperation(int code, int uid, String packageName, String featureId,
+ public int noteOperation(int code, int uid, String packageName, String attributionTag,
boolean shouldCollectAsyncNotedOp, String message) {
final CheckOpsDelegate delegate;
synchronized (this) {
delegate = mCheckOpsDelegate;
}
if (delegate == null) {
- return noteOperationImpl(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
- message);
+ return noteOperationImpl(code, uid, packageName, attributionTag,
+ shouldCollectAsyncNotedOp, message);
}
- return delegate.noteOperation(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
- message, AppOpsService.this::noteOperationImpl);
+ return delegate.noteOperation(code, uid, packageName, attributionTag,
+ shouldCollectAsyncNotedOp, message, AppOpsService.this::noteOperationImpl);
}
private int noteOperationImpl(int code, int uid, @Nullable String packageName,
- @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+ @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp,
@Nullable String message) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
@@ -2956,25 +2986,26 @@
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
- return noteOperationUnchecked(code, uid, resolvedPackageName, featureId,
+ return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF,
shouldCollectAsyncNotedOp, message);
}
private int noteOperationUnchecked(int code, int uid, @NonNull String packageName,
- @Nullable String featureId, int proxyUid, String proxyPackageName,
- @Nullable String proxyFeatureId, @OpFlags int flags, boolean shouldCollectAsyncNotedOp,
- @Nullable String message) {
+ @Nullable String attributionTag, int proxyUid, String proxyPackageName,
+ @Nullable String proxyAttributionTag, @OpFlags int flags,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message) {
RestrictionBypass bypass;
try {
- bypass = verifyAndGetBypass(uid, packageName, featureId);
+ bypass = verifyAndGetBypass(uid, packageName, attributionTag);
} catch (SecurityException e) {
Slog.e(TAG, "noteOperation", e);
return AppOpsManager.MODE_ERRORED;
}
synchronized (this) {
- final Ops ops = getOpsLocked(uid, packageName, featureId, bypass, true /* edit */);
+ final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass,
+ true /* edit */);
if (ops == null) {
scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_IGNORED);
@@ -2983,17 +3014,17 @@
return AppOpsManager.MODE_ERRORED;
}
final Op op = getOpLocked(ops, code, uid, true);
- final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
+ final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_IGNORED);
return AppOpsManager.MODE_IGNORED;
}
final UidState uidState = ops.uidState;
- if (featureOp.isRunning()) {
+ if (attributedOp.isRunning()) {
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
+ code + " startTime of in progress event="
- + featureOp.mInProgressEvents.valueAt(0).getStartTime());
+ + attributedOp.mInProgressEvents.valueAt(0).getStartTime());
}
final int switchCode = AppOpsManager.opToSwitch(code);
@@ -3005,7 +3036,7 @@
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
- featureOp.rejected(uidState.state, flags);
+ attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
return uidMode;
}
@@ -3017,26 +3048,31 @@
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
- featureOp.rejected(uidState.state, flags);
+ attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
return mode;
}
}
- if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
- + " package " + packageName + (featureId == null ? "" : "." + featureId));
- featureOp.accessed(proxyUid, proxyPackageName, proxyFeatureId, uidState.state, flags);
+ if (DEBUG) {
+ Slog.d(TAG,
+ "noteOperation: allowing code " + code + " uid " + uid + " package "
+ + packageName + (attributionTag == null ? ""
+ : "." + attributionTag));
+ }
+ attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state,
+ flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_ALLOWED);
if (shouldCollectAsyncNotedOp) {
- collectAsyncNotedOp(uid, packageName, code, featureId, message);
+ collectAsyncNotedOp(uid, packageName, code, attributionTag, message);
}
return AppOpsManager.MODE_ALLOWED;
}
}
- // TODO moltmann: Allow watching for feature ops
+ // TODO moltmann: Allow watching for attribution ops
@Override
public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
int watchedUid = -1;
@@ -3134,11 +3170,11 @@
* @param uid The uid the op was noted for
* @param packageName The package the op was noted for
* @param opCode The code of the op noted
- * @param featureId The id of the feature to op was noted for
+ * @param attributionTag attribution tag the op was noted for
* @param message The message for the op noting
*/
private void collectAsyncNotedOp(int uid, @NonNull String packageName, int opCode,
- @Nullable String featureId, @NonNull String message) {
+ @Nullable String attributionTag, @NonNull String message) {
Objects.requireNonNull(message);
int callingUid = Binder.getCallingUid();
@@ -3150,10 +3186,10 @@
RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
- featureId, message, System.currentTimeMillis());
+ attributionTag, message, System.currentTimeMillis());
final boolean[] wasNoteForwarded = {false};
- reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, featureId,
+ reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, attributionTag,
message);
if (callbacks != null) {
@@ -3164,7 +3200,7 @@
} catch (RemoteException e) {
Slog.e(TAG,
"Could not forward noteOp of " + opCode + " to " + packageName
- + "/" + uid + "(" + featureId + ")", e);
+ + "/" + uid + "(" + attributionTag + ")", e);
}
});
}
@@ -3266,7 +3302,7 @@
@Override
public int startOperation(IBinder clientId, int code, int uid, String packageName,
- String featureId, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
+ String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
String message) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
@@ -3277,14 +3313,14 @@
RestrictionBypass bypass;
try {
- bypass = verifyAndGetBypass(uid, packageName, featureId);
+ bypass = verifyAndGetBypass(uid, packageName, attributionTag);
} catch (SecurityException e) {
Slog.e(TAG, "startOperation", e);
return AppOpsManager.MODE_ERRORED;
}
synchronized (this) {
- final Ops ops = getOpsLocked(uid, resolvedPackageName, featureId, bypass,
+ final Ops ops = getOpsLocked(uid, resolvedPackageName, attributionTag, bypass,
true /* edit */);
if (ops == null) {
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
@@ -3295,7 +3331,7 @@
if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) {
return AppOpsManager.MODE_IGNORED;
}
- final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
+ final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
// If there is a non-default per UID policy (we set UID op mode only if
@@ -3308,7 +3344,7 @@
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
- featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
+ attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
return uidMode;
}
} else {
@@ -3320,21 +3356,21 @@
if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
- featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
+ attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
return mode;
}
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+ " package " + resolvedPackageName);
try {
- featureOp.started(clientId, uidState.state);
+ attributedOp.started(clientId, uidState.state);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
if (shouldCollectAsyncNotedOp) {
- collectAsyncNotedOp(uid, packageName, code, featureId, message);
+ collectAsyncNotedOp(uid, packageName, code, attributionTag, message);
}
return AppOpsManager.MODE_ALLOWED;
@@ -3342,7 +3378,7 @@
@Override
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
- String featureId) {
+ String attributionTag) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -3352,27 +3388,31 @@
RestrictionBypass bypass;
try {
- bypass = verifyAndGetBypass(uid, packageName, featureId);
+ bypass = verifyAndGetBypass(uid, packageName, attributionTag);
} catch (SecurityException e) {
Slog.e(TAG, "Cannot finishOperation", e);
return;
}
synchronized (this) {
- Op op = getOpLocked(code, uid, resolvedPackageName, featureId, bypass, true);
+ Op op = getOpLocked(code, uid, resolvedPackageName, attributionTag, bypass, true);
if (op == null) {
+ Slog.e(TAG, "Operation not found: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
return;
}
- final FeatureOp featureOp = op.mFeatures.get(featureId);
- if (featureOp == null) {
+ final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
+ if (attributedOp == null) {
+ Slog.e(TAG, "Attribution not found: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
return;
}
- try {
- featureOp.finished(clientId);
- } catch (IllegalStateException e) {
- Slog.e(TAG, "Operation not started: uid=" + uid + " pkg="
- + packageName + " op=" + AppOpsManager.opToName(code), e);
+ if (attributedOp.isRunning()) {
+ attributedOp.finished(clientId);
+ } else {
+ Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
}
}
}
@@ -3639,25 +3679,25 @@
*
* @param uid The uid the package belongs to
* @param packageName The package the might belong to the uid
- * @param featureId The feature in the package or {@code null} if no need to verify
+ * @param attributionTag attribution tag or {@code null} if no need to verify
*
* @return {@code true} iff the package is privileged
*/
private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName,
- @Nullable String featureId) {
+ @Nullable String attributionTag) {
if (uid == Process.ROOT_UID) {
// For backwards compatibility, don't check package name for root UID.
return null;
}
- // Do not check if uid/packageName/featureId is already known
+ // Do not check if uid/packageName/attributionTag is already known
synchronized (this) {
UidState uidState = mUidStates.get(uid);
if (uidState != null && uidState.pkgOps != null) {
Ops ops = uidState.pkgOps.get(packageName);
- if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))
- && ops.bypass != null) {
+ if (ops != null && (attributionTag == null || ops.knownAttributionTags.contains(
+ attributionTag)) && ops.bypass != null) {
return ops.bypass;
}
}
@@ -3669,17 +3709,17 @@
int pkgUid;
AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class).getPackage(
packageName);
- boolean isFeatureIdValid = false;
+ boolean isAttributionTagValid = false;
if (pkg != null) {
- if (featureId == null) {
- isFeatureIdValid = true;
+ if (attributionTag == null) {
+ isAttributionTagValid = true;
} else {
- if (pkg.getFeatures() != null) {
- int numFeatures = pkg.getFeatures().size();
- for (int i = 0; i < numFeatures; i++) {
- if (pkg.getFeatures().get(i).id.equals(featureId)) {
- isFeatureIdValid = true;
+ if (pkg.getAttributions() != null) {
+ int numAttributions = pkg.getAttributions().size();
+ for (int i = 0; i < numAttributions; i++) {
+ if (pkg.getAttributions().get(i).tag.equals(attributionTag)) {
+ isAttributionTagValid = true;
}
}
}
@@ -3689,8 +3729,8 @@
UserHandle.getUserId(uid), UserHandle.getAppId(pkg.getUid()));
bypass = getBypassforPackage(pkg);
} else {
- // Allow any feature id for resolvable uids
- isFeatureIdValid = true;
+ // Allow any attribution tag for resolvable uids
+ isAttributionTagValid = true;
pkgUid = resolveUid(packageName);
if (pkgUid >= 0) {
@@ -3702,9 +3742,9 @@
+ " but it is really " + pkgUid);
}
- if (!isFeatureIdValid) {
+ if (!isAttributionTagValid) {
// TODO moltmann: Switch from logging to enforcement
- Slog.e(TAG, "featureId " + featureId + " not declared in manifest of "
+ Slog.e(TAG, "attributionTag " + attributionTag + " not declared in manifest of "
+ packageName);
}
} finally {
@@ -3719,13 +3759,13 @@
*
* @param uid The uid the package belongs to
* @param packageName The name of the package
- * @param featureId The feature in the package
+ * @param attributionTag attribution tag
* @param bypass When to bypass certain op restrictions (can be null if edit == false)
* @param edit If an ops does not exist, create the ops?
* @return The ops
*/
- private Ops getOpsLocked(int uid, String packageName, @Nullable String featureId,
+ private Ops getOpsLocked(int uid, String packageName, @Nullable String attributionTag,
@Nullable RestrictionBypass bypass, boolean edit) {
UidState uidState = getUidStateLocked(uid, edit);
if (uidState == null) {
@@ -3753,8 +3793,8 @@
ops.bypass = bypass;
}
- if (featureId != null) {
- ops.knownFeatureIds.add(featureId);
+ if (attributionTag != null) {
+ ops.knownAttributionTags.add(attributionTag);
}
}
@@ -3783,15 +3823,15 @@
* @param code The code of the op
* @param uid The uid the of the package
* @param packageName The package name for which to get the state for
- * @param featureId The feature in the package
+ * @param attributionTag The attribution tag
* @param bypass When to bypass certain op restrictions (can be null if edit == false)
* @param edit Iff {@code true} create the {@link Op} object if not yet created
*
* @return The {@link Op state} of the op
*/
private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
- @Nullable String featureId, @Nullable RestrictionBypass bypass, boolean edit) {
- Ops ops = getOpsLocked(uid, packageName, featureId, bypass, edit);
+ @Nullable String attributionTag, @Nullable RestrictionBypass bypass, boolean edit) {
+ Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, edit);
if (ops == null) {
return null;
}
@@ -4048,9 +4088,9 @@
uidState.evalForegroundOps(mOpModeWatchers);
}
- private void readFeatureOp(XmlPullParser parser, @NonNull Op parent,
- @Nullable String feature) throws NumberFormatException, IOException {
- final FeatureOp featureOp = parent.getOrCreateFeature(parent, feature);
+ private void readAttributionOp(XmlPullParser parser, @NonNull Op parent,
+ @Nullable String attribution) throws NumberFormatException, IOException {
+ final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution);
final long key = XmlUtils.readLongAttribute(parser, "n");
final int uidState = extractUidStateFromKey(key);
@@ -4061,14 +4101,14 @@
final long accessDuration = XmlUtils.readLongAttribute(parser, "d", -1);
final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", Process.INVALID_UID);
- final String proxyFeatureId = XmlUtils.readStringAttribute(parser, "pc");
+ final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc");
if (accessTime > 0) {
- featureOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg, proxyFeatureId,
- uidState, opFlags);
+ attributedOp.accessed(accessTime, accessDuration, proxyUid, proxyPkg,
+ proxyAttributionTag, uidState, opFlags);
}
if (rejectTime > 0) {
- featureOp.rejected(rejectTime, uidState, opFlags);
+ attributedOp.rejected(rejectTime, uidState, opFlags);
}
}
@@ -4094,7 +4134,7 @@
}
String tagName = parser.getName();
if (tagName.equals("st")) {
- readFeatureOp(parser, op, XmlUtils.readStringAttribute(parser, "id"));
+ readAttributionOp(parser, op, XmlUtils.readStringAttribute(parser, "id"));
} else {
Slog.w(TAG, "Unknown element under <op>: "
+ parser.getName());
@@ -4208,11 +4248,11 @@
out.attribute(null, "m", Integer.toString(op.getMode()));
}
- for (String featureId : op.getFeatures().keySet()) {
- final OpFeatureEntry feature = op.getFeatures().get(
- featureId);
+ for (String attributionTag : op.getAttributedOpEntries().keySet()) {
+ final AttributedOpEntry attribution =
+ op.getAttributedOpEntries().get(attributionTag);
- final ArraySet<Long> keys = feature.collectKeys();
+ final ArraySet<Long> keys = attribution.collectKeys();
final int keyCount = keys.size();
for (int k = 0; k < keyCount; k++) {
@@ -4221,14 +4261,14 @@
final int uidState = AppOpsManager.extractUidStateFromKey(key);
final int flags = AppOpsManager.extractFlagsFromKey(key);
- final long accessTime = feature.getLastAccessTime(uidState,
+ final long accessTime = attribution.getLastAccessTime(uidState,
uidState, flags);
- final long rejectTime = feature.getLastRejectTime(uidState,
+ final long rejectTime = attribution.getLastRejectTime(uidState,
uidState, flags);
- final long accessDuration = feature.getLastDuration(uidState,
- uidState, flags);
+ final long accessDuration = attribution.getLastDuration(
+ uidState, uidState, flags);
// Proxy information for rejections is not backed up
- final OpEventProxyInfo proxy = feature.getLastProxyInfo(
+ final OpEventProxyInfo proxy = attribution.getLastProxyInfo(
uidState, uidState, flags);
if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0
@@ -4237,17 +4277,17 @@
}
String proxyPkg = null;
- String proxyFeatureId = null;
+ String proxyAttributionTag = null;
int proxyUid = Process.INVALID_UID;
if (proxy != null) {
proxyPkg = proxy.getPackageName();
- proxyFeatureId = proxy.getFeatureId();
+ proxyAttributionTag = proxy.getAttributionTag();
proxyUid = proxy.getUid();
}
out.startTag(null, "st");
- if (featureId != null) {
- out.attribute(null, "id", featureId);
+ if (attributionTag != null) {
+ out.attribute(null, "id", attributionTag);
}
out.attribute(null, "n", Long.toString(key));
if (accessTime > 0) {
@@ -4262,8 +4302,8 @@
if (proxyPkg != null) {
out.attribute(null, "pp", proxyPkg);
}
- if (proxyFeatureId != null) {
- out.attribute(null, "pc", proxyFeatureId);
+ if (proxyAttributionTag != null) {
+ out.attribute(null, "pc", proxyAttributionTag);
}
if (proxyUid >= 0) {
out.attribute(null, "pu", Integer.toString(proxyUid));
@@ -4297,7 +4337,7 @@
int userId = UserHandle.USER_SYSTEM;
String packageName;
- String featureId;
+ String attributionTag;
String opStr;
String modeStr;
int op;
@@ -4399,8 +4439,8 @@
userId = UserHandle.parseUserArg(getNextArgRequired());
} else if ("--uid".equals(argument)) {
targetsUid = true;
- } else if ("--feature".equals(argument)) {
- featureId = getNextArgRequired();
+ } else if ("--attribution".equals(argument)) {
+ attributionTag = getNextArgRequired();
} else {
if (packageName == null) {
packageName = argument;
@@ -4495,13 +4535,16 @@
pw.println("AppOps service (appops) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" start [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> <OP> ");
+ pw.println(" start [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> "
+ + "<OP> ");
pw.println(" Starts a given operation for a particular application.");
- pw.println(" stop [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> <OP> ");
+ pw.println(" stop [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> "
+ + "<OP> ");
pw.println(" Stops a given operation for a particular application.");
pw.println(" set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>");
pw.println(" Set the mode for a particular application and operation.");
- pw.println(" get [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> [<OP>]");
+ pw.println(" get [--user <USER_ID>] [--attribution <ATTRIBUTION_TAG>] <PACKAGE | UID> "
+ + "[<OP>]");
pw.println(" Return the mode for a particular application and optional operation.");
pw.println(" query-op [--user <USER_ID>] <OP> [<MODE>]");
pw.println(" Print all packages that currently have the given op in the given mode.");
@@ -4515,8 +4558,8 @@
pw.println(" <PACKAGE> an Android package name or its UID if prefixed by --uid");
pw.println(" <OP> an AppOps operation.");
pw.println(" <MODE> one of allow, ignore, deny, or default");
- pw.println(" <USER_ID> the user id under which the package is installed. If --user is not");
- pw.println(" specified, the current user is assumed.");
+ pw.println(" <USER_ID> the user id under which the package is installed. If --user is");
+ pw.println(" not specified, the current user is assumed.");
}
static int onShellCommand(Shell shell, String cmd) {
@@ -4605,7 +4648,7 @@
pw.print(AppOpsManager.opToName(ent.getOp()));
pw.print(": ");
pw.print(AppOpsManager.modeToName(ent.getMode()));
- if (shell.featureId == null) {
+ if (shell.attributionTag == null) {
if (ent.getLastAccessTime(OP_FLAGS_ALL) != -1) {
pw.print("; time=");
TimeUtils.formatDuration(
@@ -4625,29 +4668,30 @@
TimeUtils.formatDuration(ent.getLastDuration(OP_FLAGS_ALL), pw);
}
} else {
- final OpFeatureEntry featureEnt = ent.getFeatures().get(
- shell.featureId);
- if (featureEnt != null) {
- if (featureEnt.getLastAccessTime(OP_FLAGS_ALL) != -1) {
+ final AppOpsManager.AttributedOpEntry attributionEnt =
+ ent.getAttributedOpEntries().get(shell.attributionTag);
+ if (attributionEnt != null) {
+ if (attributionEnt.getLastAccessTime(OP_FLAGS_ALL) != -1) {
pw.print("; time=");
- TimeUtils.formatDuration(now - featureEnt.getLastAccessTime(
- OP_FLAGS_ALL), pw);
+ TimeUtils.formatDuration(
+ now - attributionEnt.getLastAccessTime(
+ OP_FLAGS_ALL), pw);
pw.print(" ago");
}
- if (featureEnt.getLastRejectTime(OP_FLAGS_ALL) != -1) {
+ if (attributionEnt.getLastRejectTime(OP_FLAGS_ALL) != -1) {
pw.print("; rejectTime=");
TimeUtils.formatDuration(
- now - featureEnt.getLastRejectTime(OP_FLAGS_ALL),
- pw);
+ now - attributionEnt.getLastRejectTime(
+ OP_FLAGS_ALL), pw);
pw.print(" ago");
}
- if (featureEnt.isRunning()) {
+ if (attributionEnt.isRunning()) {
pw.print(" (running)");
- } else if (featureEnt.getLastDuration(OP_FLAGS_ALL)
+ } else if (attributionEnt.getLastDuration(OP_FLAGS_ALL)
!= -1) {
pw.print("; duration=");
TimeUtils.formatDuration(
- featureEnt.getLastDuration(OP_FLAGS_ALL), pw);
+ attributionEnt.getLastDuration(OP_FLAGS_ALL), pw);
}
}
}
@@ -4755,7 +4799,7 @@
if (shell.packageName != null) {
shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
- shell.packageName, shell.featureId, true, true,
+ shell.packageName, shell.attributionTag, true, true,
"appops start shell command");
} else {
return -1;
@@ -4769,8 +4813,8 @@
}
if (shell.packageName != null) {
- shell.mInterface.finishOperation(shell.mToken,
- shell.op, shell.packageUid, shell.packageName, shell.featureId);
+ shell.mInterface.finishOperation(shell.mToken, shell.op, shell.packageUid,
+ shell.packageName, shell.attributionTag);
} else {
return -1;
}
@@ -4795,35 +4839,35 @@
pw.println(" Limit output to data associated with the given app op mode.");
pw.println(" --package [PACKAGE]");
pw.println(" Limit output to data associated with the given package name.");
- pw.println(" --featureId [featureId]");
- pw.println(" Limit output to data associated with the given feature id.");
+ pw.println(" --attributionTag [attributionTag]");
+ pw.println(" Limit output to data associated with the given attribution tag.");
pw.println(" --watchers");
pw.println(" Only output the watcher sections.");
}
- private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterFeatureId,
+ private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag,
@HistoricalOpsRequestFilter int filter, long nowElapsed, @NonNull Op op, long now,
@NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
- final int numFeatures = op.mFeatures.size();
- for (int i = 0; i < numFeatures; i++) {
- if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(op.mFeatures.keyAt(i),
- filterFeatureId)) {
+ final int numAttributions = op.mAttributions.size();
+ for (int i = 0; i < numAttributions; i++) {
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(
+ op.mAttributions.keyAt(i), filterAttributionTag)) {
continue;
}
- pw.print(prefix + op.mFeatures.keyAt(i) + "=[\n");
- dumpStatesLocked(pw, nowElapsed, op, op.mFeatures.keyAt(i), now, sdf, date,
+ pw.print(prefix + op.mAttributions.keyAt(i) + "=[\n");
+ dumpStatesLocked(pw, nowElapsed, op, op.mAttributions.keyAt(i), now, sdf, date,
prefix + " ");
pw.print(prefix + "]\n");
}
}
private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op,
- @Nullable String featureId, long now, @NonNull SimpleDateFormat sdf,
+ @Nullable String attributionTag, long now, @NonNull SimpleDateFormat sdf,
@NonNull Date date, @NonNull String prefix) {
- final OpFeatureEntry entry = op.createSingleFeatureEntryLocked(
- featureId).getFeatures().get(featureId);
+ final AttributedOpEntry entry = op.createSingleAttributionEntryLocked(
+ attributionTag).getAttributedOpEntries().get(attributionTag);
final ArraySet<Long> keys = entry.collectKeys();
@@ -4840,11 +4884,11 @@
final OpEventProxyInfo proxy = entry.getLastProxyInfo(uidState, uidState, flags);
String proxyPkg = null;
- String proxyFeatureId = null;
+ String proxyAttributionTag = null;
int proxyUid = Process.INVALID_UID;
if (proxy != null) {
proxyPkg = proxy.getPackageName();
- proxyFeatureId = proxy.getFeatureId();
+ proxyAttributionTag = proxy.getAttributionTag();
proxyUid = proxy.getUid();
}
@@ -4868,8 +4912,8 @@
pw.print(proxyUid);
pw.print(", pkg=");
pw.print(proxyPkg);
- pw.print(", feature=");
- pw.print(proxyFeatureId);
+ pw.print(", attributionTag=");
+ pw.print(proxyAttributionTag);
pw.print("]");
}
pw.println();
@@ -4890,21 +4934,21 @@
pw.print(proxyUid);
pw.print(", pkg=");
pw.print(proxyPkg);
- pw.print(", feature=");
- pw.print(proxyFeatureId);
+ pw.print(", attributionTag=");
+ pw.print(proxyAttributionTag);
pw.print("]");
}
pw.println();
}
}
- final FeatureOp featureOp = op.mFeatures.get(featureId);
- if (featureOp.isRunning()) {
+ final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
+ if (attributedOp.isRunning()) {
long earliestElapsedTime = Long.MAX_VALUE;
long maxNumStarts = 0;
- int numInProgressEvents = featureOp.mInProgressEvents.size();
+ int numInProgressEvents = attributedOp.mInProgressEvents.size();
for (int i = 0; i < numInProgressEvents; i++) {
- InProgressStartOpEvent event = featureOp.mInProgressEvents.valueAt(i);
+ InProgressStartOpEvent event = attributedOp.mInProgressEvents.valueAt(i);
earliestElapsedTime = Math.min(earliestElapsedTime, event.getStartElapsedTime());
maxNumStarts = Math.max(maxNumStarts, event.numUnfinishedStarts);
@@ -4927,7 +4971,7 @@
int dumpOp = OP_NONE;
String dumpPackage = null;
- String dumpFeatureId = null;
+ String dumpAttributionTag = null;
int dumpUid = Process.INVALID_UID;
int dumpMode = -1;
boolean dumpWatchers = false;
@@ -4974,14 +5018,14 @@
}
dumpUid = UserHandle.getAppId(dumpUid);
dumpFilter |= FILTER_BY_UID;
- } else if ("--featureId".equals(arg)) {
+ } else if ("--attributionTag".equals(arg)) {
i++;
if (i >= args.length) {
- pw.println("No argument for --featureId option");
+ pw.println("No argument for --attributionTag option");
return;
}
- dumpFeatureId = args[i];
- dumpFilter |= FILTER_BY_FEATURE_ID;
+ dumpAttributionTag = args[i];
+ dumpFilter |= FILTER_BY_ATTRIBUTION_TAG;
} else if ("--mode".equals(arg)) {
i++;
if (i >= args.length) {
@@ -5320,8 +5364,8 @@
pw.print("="); pw.print(AppOpsManager.modeToName(mode));
}
pw.println("): ");
- dumpStatesLocked(pw, dumpFeatureId, dumpFilter, nowElapsed, op, now, sdf,
- date, " ");
+ dumpStatesLocked(pw, dumpAttributionTag, dumpFilter, nowElapsed, op, now,
+ sdf, date, " ");
}
}
}
@@ -5420,7 +5464,7 @@
// Must not hold the appops lock
if (dumpHistory && !dumpWatchers) {
- mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpFeatureId, dumpOp,
+ mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpAttributionTag, dumpOp,
dumpFilter);
}
}
@@ -5525,7 +5569,7 @@
if (resolvedPackageName == null) {
return false;
}
- // TODO moltmann: Allow to check for feature op activeness
+ // TODO moltmann: Allow to check for attribution op activeness
synchronized (AppOpsService.this) {
Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false);
if (pkgOps == null) {
@@ -5586,7 +5630,7 @@
* Report runtime access to AppOp together with message (including stack trace)
*
* @param packageName The package which reported the op
- * @param notedAppOp contains code of op and featureId provided by developer
+ * @param notedAppOp contains code of op and attributionTag provided by developer
* @param message Message describing AppOp access (can be stack trace)
*
* @return Config for future sampling to reduce amount of reporting
@@ -5597,7 +5641,7 @@
int uid = Binder.getCallingUid();
Objects.requireNonNull(packageName);
synchronized (this) {
- switchPackageIfRarelyUsedLocked(packageName);
+ switchPackageIfBootTimeOrRarelyUsedLocked(packageName);
if (!packageName.equals(mSampledPackage)) {
return new MessageSamplingConfig(OP_NONE, 0,
Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
@@ -5608,7 +5652,7 @@
reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName,
AppOpsManager.strOpToOp(notedAppOp.getOp()),
- notedAppOp.getFeatureId(), message);
+ notedAppOp.getAttributionTag(), message);
return new MessageSamplingConfig(mSampledAppOpCode, mAcceptableLeftDistance,
Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
@@ -5621,17 +5665,18 @@
* @param uid Uid of the package which reported the op
* @param packageName The package which reported the op
* @param opCode Code of AppOp
- * @param featureId FeautreId of AppOp reported
+ * @param attributionTag FeautreId of AppOp reported
* @param message Message describing AppOp access (can be stack trace)
*/
private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid,
- @NonNull String packageName, int opCode, @Nullable String featureId,
+ @NonNull String packageName, int opCode, @Nullable String attributionTag,
@NonNull String message) {
- switchPackageIfRarelyUsedLocked(packageName);
+ switchPackageIfBootTimeOrRarelyUsedLocked(packageName);
if (!Objects.equals(mSampledPackage, packageName)) {
return;
}
- reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, featureId, message);
+ reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, attributionTag,
+ message);
}
/**
@@ -5639,7 +5684,7 @@
* reporting uniformly at random across all received messages.
*/
private void reportRuntimeAppOpAccessMessageInternalLocked(int uid,
- @NonNull String packageName, int opCode, @Nullable String featureId,
+ @NonNull String packageName, int opCode, @Nullable String attributionTag,
@NonNull String message) {
int newLeftDistance = AppOpsManager.leftCircularDistance(opCode,
mSampledAppOpCode, _NUM_OP);
@@ -5656,7 +5701,7 @@
mMessagesCollectedCount += 1.0f;
if (ThreadLocalRandom.current().nextFloat() <= 1.0f / mMessagesCollectedCount) {
mCollectedRuntimePermissionMessage = new RuntimeAppOpAccessMessage(uid, opCode,
- packageName, featureId, message, mSamplingStrategy);
+ packageName, attributionTag, message, mSamplingStrategy);
}
return;
}
@@ -5677,11 +5722,16 @@
/**
* Checks if package is in the list of rarely used package and starts watching the new package
- * to collect incoming message.
+ * to collect incoming message or if collection is happening in first minutes since boot.
* @param packageName
*/
- private void switchPackageIfRarelyUsedLocked(@NonNull String packageName) {
- if (mRarelyUsedPackages.contains(packageName)) {
+ private void switchPackageIfBootTimeOrRarelyUsedLocked(@NonNull String packageName) {
+ if (mSampledPackage == null) {
+ if (ThreadLocalRandom.current().nextFloat() < 0.1f) {
+ mSamplingStrategy = SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
+ resampleAppOpForPackageLocked(packageName);
+ }
+ } else if (mRarelyUsedPackages.contains(packageName)) {
mRarelyUsedPackages.remove(packageName);
if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
mSamplingStrategy = SAMPLING_STRATEGY_RARELY_USED;
@@ -5743,6 +5793,10 @@
}
}
synchronized (this) {
+ int numPkgs = mRarelyUsedPackages.size();
+ for (int i = 0; i < numPkgs; i++) {
+ candidates.add(mRarelyUsedPackages.valueAt(i));
+ }
mRarelyUsedPackages = candidates;
}
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index cd450d4..ed45069 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -15,7 +15,7 @@
*/
package com.android.server.appop;
-import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
+import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
-import android.app.AppOpsManager.HistoricalFeatureOps;
import android.app.AppOpsManager.HistoricalMode;
import android.app.AppOpsManager.HistoricalOp;
import android.app.AppOpsManager.HistoricalOps;
@@ -282,7 +281,7 @@
}
void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage,
- @Nullable String filterFeatureId, int filterOp,
+ @Nullable String filterAttributionTag, int filterOp,
@HistoricalOpsRequestFilter int filter) {
if (!isApiEnabled()) {
return;
@@ -298,7 +297,7 @@
pw.println(AppOpsManager.historicalModeToString(mMode));
final StringDumpVisitor visitor = new StringDumpVisitor(prefix + " ",
- pw, filterUid, filterPackage, filterFeatureId, filterOp, filter);
+ pw, filterUid, filterPackage, filterAttributionTag, filterOp, filter);
final long nowMillis = System.currentTimeMillis();
// Dump in memory state first
@@ -338,7 +337,7 @@
}
void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
- @Nullable String featureId, @Nullable String[] opNames,
+ @Nullable String attributionTag, @Nullable String[] opNames,
@HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
@OpFlags int flags, @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
@@ -354,7 +353,7 @@
return;
}
final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
- mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId,
+ mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, attributionTag,
opNames, filter, beginTimeMillis, endTimeMillis, flags);
final Bundle payload = new Bundle();
payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
@@ -363,7 +362,7 @@
}
}
- void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String featureId,
+ void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String attributionTag,
@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
@NonNull RemoteCallback callback) {
@@ -401,7 +400,7 @@
|| inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) {
// Some of the current batch falls into the query, so extract that.
final HistoricalOps currentOpsCopy = new HistoricalOps(currentOps);
- currentOpsCopy.filter(uid, packageName, featureId, opNames, filter,
+ currentOpsCopy.filter(uid, packageName, attributionTag, opNames, filter,
inMemoryAdjBeginTimeMillis, inMemoryAdjEndTimeMillis);
result.merge(currentOpsCopy);
}
@@ -421,7 +420,7 @@
- onDiskAndInMemoryOffsetMillis, 0);
final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis
- onDiskAndInMemoryOffsetMillis, 0);
- mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId,
+ mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, attributionTag,
opNames, filter, onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags);
}
@@ -436,7 +435,7 @@
}
void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags) {
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -445,13 +444,13 @@
}
getUpdatedPendingHistoricalOpsMLocked(
System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
- featureId, uidState, flags, 1);
+ attributionTag, uidState, flags, 1);
}
}
}
void incrementOpRejected(int op, int uid, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags) {
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -460,13 +459,13 @@
}
getUpdatedPendingHistoricalOpsMLocked(
System.currentTimeMillis()).increaseRejectCount(op, uid, packageName,
- featureId, uidState, flags, 1);
+ attributionTag, uidState, flags, 1);
}
}
}
void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
- @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
long increment) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
@@ -476,7 +475,7 @@
}
getUpdatedPendingHistoricalOpsMLocked(
System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
- featureId, uidState, flags, increment);
+ attributionTag, uidState, flags, increment);
}
}
}
@@ -728,7 +727,7 @@
private static final String TAG_OPS = "ops";
private static final String TAG_UID = "uid";
private static final String TAG_PACKAGE = "pkg";
- private static final String TAG_FEATURE = "ftr";
+ private static final String TAG_ATTRIBUTION = "ftr";
private static final String TAG_OP = "op";
private static final String TAG_STATE = "st";
@@ -807,9 +806,9 @@
@Nullable List<HistoricalOps> readHistoryRawDLocked() {
return collectHistoricalOpsBaseDLocked(Process.INVALID_UID /*filterUid*/,
- null /*filterPackageName*/, null /*filterFeatureId*/, null /*filterOpNames*/,
- 0 /*filter*/, 0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/,
- AppOpsManager.OP_FLAGS_ALL);
+ null /*filterPackageName*/, null /*filterAttributionTag*/,
+ null /*filterOpNames*/, 0 /*filter*/, 0 /*filterBeginTimeMills*/,
+ Long.MAX_VALUE /*filterEndTimeMills*/, AppOpsManager.OP_FLAGS_ALL);
}
@Nullable List<HistoricalOps> readHistoryDLocked() {
@@ -861,13 +860,13 @@
return 0;
}
- private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps,
- int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps, int filterUid,
+ @Nullable String filterPackageName, @Nullable String filterAttributionTag,
@Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
long filterBeingMillis, long filterEndMillis, @OpFlags int filterFlags) {
final List<HistoricalOps> readOps = collectHistoricalOpsBaseDLocked(filterUid,
- filterPackageName, filterFeatureId, filterOpNames, filter, filterBeingMillis,
- filterEndMillis, filterFlags);
+ filterPackageName, filterAttributionTag, filterOpNames, filter,
+ filterBeingMillis, filterEndMillis, filterFlags);
if (readOps != null) {
final int readCount = readOps.size();
for (int i = 0; i < readCount; i++) {
@@ -877,8 +876,8 @@
}
}
- private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(
- int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(int filterUid,
+ @Nullable String filterPackageName, @Nullable String filterAttributionTag,
@Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) {
File baseDir = null;
@@ -892,7 +891,7 @@
final Set<String> historyFiles = getHistoricalFileNames(baseDir);
final long[] globalContentOffsetMillis = {0};
final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked(
- baseDir, filterUid, filterPackageName, filterFeatureId, filterOpNames,
+ baseDir, filterUid, filterPackageName, filterAttributionTag, filterOpNames,
filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
globalContentOffsetMillis, null /*outOps*/, 0 /*depth*/, historyFiles);
if (DEBUG) {
@@ -909,7 +908,7 @@
private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsRecursiveDLocked(
@NonNull File baseDir, int filterUid, @Nullable String filterPackageName,
- @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+ @Nullable String filterAttributionTag, @Nullable String[] filterOpNames,
@HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
long filterEndTimeMillis, @OpFlags int filterFlags,
@NonNull long[] globalContentOffsetMillis,
@@ -927,7 +926,7 @@
// Read historical data at this level
final List<HistoricalOps> readOps = readHistoricalOpsLocked(baseDir,
previousIntervalEndMillis, currentIntervalEndMillis, filterUid,
- filterPackageName, filterFeatureId, filterOpNames, filter,
+ filterPackageName, filterAttributionTag, filterOpNames, filter,
filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
globalContentOffsetMillis, depth, historyFiles);
// Empty is a special signal to stop diving
@@ -937,7 +936,7 @@
// Collect older historical data from subsequent levels
outOps = collectHistoricalOpsRecursiveDLocked(baseDir, filterUid, filterPackageName,
- filterFeatureId, filterOpNames, filter, filterBeginTimeMillis,
+ filterAttributionTag, filterOpNames, filter, filterBeginTimeMillis,
filterEndTimeMillis, filterFlags, globalContentOffsetMillis, outOps, depth + 1,
historyFiles);
@@ -1006,7 +1005,7 @@
final List<HistoricalOps> existingOps = readHistoricalOpsLocked(oldBaseDir,
previousIntervalEndMillis, currentIntervalEndMillis,
Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/,
- null /*filterFeatureId*/, null /*filterOpNames*/, 0 /*filter*/,
+ null /*filterAttributionTag*/, null /*filterOpNames*/, 0 /*filter*/,
Long.MIN_VALUE /*filterBeginTimeMillis*/,
Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL, null, depth,
null /*historyFiles*/);
@@ -1120,7 +1119,7 @@
private @Nullable List<HistoricalOps> readHistoricalOpsLocked(File baseDir,
long intervalBeginMillis, long intervalEndMillis, int filterUid,
- @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String filterPackageName, @Nullable String filterAttributionTag,
@Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
@Nullable long[] cumulativeOverflowMillis, int depth,
@@ -1147,15 +1146,16 @@
return null;
}
}
- return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterFeatureId,
+ return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterAttributionTag,
filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
cumulativeOverflowMillis);
}
- private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file,
- int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
- @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
- long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
+ private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file,
+ int filterUid, @Nullable String filterPackageName,
+ @Nullable String filterAttributionTag, @Nullable String[] filterOpNames,
+ @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
+ long filterEndTimeMillis, @OpFlags int filterFlags,
@Nullable long[] cumulativeOverflowMillis)
throws IOException, XmlPullParserException {
if (DEBUG) {
@@ -1180,7 +1180,7 @@
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_OPS.equals(parser.getName())) {
final HistoricalOps ops = readeHistoricalOpsDLocked(parser, filterUid,
- filterPackageName, filterFeatureId, filterOpNames, filter,
+ filterPackageName, filterAttributionTag, filterOpNames, filter,
filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
cumulativeOverflowMillis);
if (ops == null) {
@@ -1215,7 +1215,7 @@
private @Nullable HistoricalOps readeHistoricalOpsDLocked(
@NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName,
- @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+ @Nullable String filterAttributionTag, @Nullable String[] filterOpNames,
@HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
long filterEndTimeMillis, @OpFlags int filterFlags,
@Nullable long[] cumulativeOverflowMillis)
@@ -1245,8 +1245,8 @@
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_UID.equals(parser.getName())) {
final HistoricalOps returnedOps = readHistoricalUidOpsDLocked(ops, parser,
- filterUid, filterPackageName, filterFeatureId, filterOpNames, filter,
- filterFlags, filterScale);
+ filterUid, filterPackageName, filterAttributionTag, filterOpNames,
+ filter, filterFlags, filterScale);
if (ops == null) {
ops = returnedOps;
}
@@ -1260,7 +1260,7 @@
private @Nullable HistoricalOps readHistoricalUidOpsDLocked(
@Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid,
- @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String filterPackageName, @Nullable String filterAttributionTag,
@Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
@OpFlags int filterFlags, double filterScale)
throws IOException, XmlPullParserException {
@@ -1272,8 +1272,8 @@
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_PACKAGE.equals(parser.getName())) {
- final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops,
- uid, parser, filterPackageName, filterFeatureId, filterOpNames, filter,
+ final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops, uid,
+ parser, filterPackageName, filterAttributionTag, filterOpNames, filter,
filterFlags, filterScale);
if (ops == null) {
ops = returnedOps;
@@ -1285,7 +1285,7 @@
private @Nullable HistoricalOps readHistoricalPackageOpsDLocked(
@Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser,
- @Nullable String filterPackageName, @Nullable String filterFeatureId,
+ @Nullable String filterPackageName, @Nullable String filterAttributionTag,
@Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
@OpFlags int filterFlags, double filterScale)
throws IOException, XmlPullParserException {
@@ -1296,9 +1296,9 @@
}
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
- if (TAG_FEATURE.equals(parser.getName())) {
- final HistoricalOps returnedOps = readHistoricalFeatureOpsDLocked(ops, uid,
- packageName, parser, filterFeatureId, filterOpNames, filter,
+ if (TAG_ATTRIBUTION.equals(parser.getName())) {
+ final HistoricalOps returnedOps = readHistoricalAttributionOpsDLocked(ops, uid,
+ packageName, parser, filterAttributionTag, filterOpNames, filter,
filterFlags, filterScale);
if (ops == null) {
ops = returnedOps;
@@ -1308,15 +1308,15 @@
return ops;
}
- private @Nullable HistoricalOps readHistoricalFeatureOpsDLocked(@Nullable HistoricalOps ops,
- int uid, String packageName, @NonNull XmlPullParser parser,
- @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
- @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
- double filterScale)
+ private @Nullable HistoricalOps readHistoricalAttributionOpsDLocked(
+ @Nullable HistoricalOps ops, int uid, String packageName,
+ @NonNull XmlPullParser parser, @Nullable String filterAttributionTag,
+ @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
+ @OpFlags int filterFlags, double filterScale)
throws IOException, XmlPullParserException {
- final String featureId = XmlUtils.readStringAttribute(parser, ATTR_NAME);
- if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(filterFeatureId,
- featureId)) {
+ final String attributionTag = XmlUtils.readStringAttribute(parser, ATTR_NAME);
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(filterAttributionTag,
+ attributionTag)) {
XmlUtils.skipCurrentTag(parser);
return null;
}
@@ -1324,7 +1324,8 @@
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_OP.equals(parser.getName())) {
final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid, packageName,
- featureId, parser, filterOpNames, filter, filterFlags, filterScale);
+ attributionTag, parser, filterOpNames, filter, filterFlags,
+ filterScale);
if (ops == null) {
ops = returnedOps;
}
@@ -1334,7 +1335,7 @@
}
private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops,
- int uid, @NonNull String packageName, @Nullable String featureId,
+ int uid, @NonNull String packageName, @Nullable String attributionTag,
@NonNull XmlPullParser parser, @Nullable String[] filterOpNames,
@HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
double filterScale)
@@ -1349,7 +1350,7 @@
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_STATE.equals(parser.getName())) {
final HistoricalOps returnedOps = readStateDLocked(ops, uid,
- packageName, featureId, op, parser, filterFlags, filterScale);
+ packageName, attributionTag, op, parser, filterFlags, filterScale);
if (ops == null) {
ops = returnedOps;
}
@@ -1359,7 +1360,7 @@
}
private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops,
- int uid, @NonNull String packageName, @Nullable String featureId, int op,
+ int uid, @NonNull String packageName, @Nullable String attributionTag, int op,
@NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale)
throws IOException {
final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME);
@@ -1377,7 +1378,7 @@
if (ops == null) {
ops = new HistoricalOps(0, 0);
}
- ops.increaseAccessCount(op, uid, packageName, featureId, uidState, flags,
+ ops.increaseAccessCount(op, uid, packageName, attributionTag, uidState, flags,
accessCount);
}
long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0);
@@ -1389,7 +1390,7 @@
if (ops == null) {
ops = new HistoricalOps(0, 0);
}
- ops.increaseRejectCount(op, uid, packageName, featureId, uidState, flags,
+ ops.increaseRejectCount(op, uid, packageName, attributionTag, uidState, flags,
rejectCount);
}
long accessDuration = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0);
@@ -1401,7 +1402,7 @@
if (ops == null) {
ops = new HistoricalOps(0, 0);
}
- ops.increaseAccessDuration(op, uid, packageName, featureId, uidState, flags,
+ ops.increaseAccessDuration(op, uid, packageName, attributionTag, uidState, flags,
accessDuration);
}
return ops;
@@ -1467,24 +1468,25 @@
@NonNull XmlSerializer serializer) throws IOException {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
- final int numFeatures = packageOps.getFeatureCount();
- for (int i = 0; i < numFeatures; i++) {
- final HistoricalFeatureOps op = packageOps.getFeatureOpsAt(i);
- writeHistoricalFeatureOpsDLocked(op, serializer);
+ final int numAttributions = packageOps.getAttributedOpsCount();
+ for (int i = 0; i < numAttributions; i++) {
+ final AppOpsManager.AttributedHistoricalOps op = packageOps.getAttributedOpsAt(i);
+ writeHistoricalAttributionOpsDLocked(op, serializer);
}
serializer.endTag(null, TAG_PACKAGE);
}
- private void writeHistoricalFeatureOpsDLocked(@NonNull HistoricalFeatureOps featureOps,
+ private void writeHistoricalAttributionOpsDLocked(
+ @NonNull AppOpsManager.AttributedHistoricalOps attributionOps,
@NonNull XmlSerializer serializer) throws IOException {
- serializer.startTag(null, TAG_FEATURE);
- XmlUtils.writeStringAttribute(serializer, ATTR_NAME, featureOps.getFeatureId());
- final int opCount = featureOps.getOpCount();
+ serializer.startTag(null, TAG_ATTRIBUTION);
+ XmlUtils.writeStringAttribute(serializer, ATTR_NAME, attributionOps.getTag());
+ final int opCount = attributionOps.getOpCount();
for (int i = 0; i < opCount; i++) {
- final HistoricalOp op = featureOps.getOpAt(i);
+ final HistoricalOp op = attributionOps.getOpAt(i);
writeHistoricalOpDLocked(op, serializer);
}
- serializer.endTag(null, TAG_FEATURE);
+ serializer.endTag(null, TAG_ATTRIBUTION);
}
private void writeHistoricalOpDLocked(@NonNull HistoricalOp op,
@@ -1718,29 +1720,29 @@
private final @NonNull String mOpsPrefix;
private final @NonNull String mUidPrefix;
private final @NonNull String mPackagePrefix;
- private final @NonNull String mFeaturePrefix;
+ private final @NonNull String mAttributionPrefix;
private final @NonNull String mEntryPrefix;
private final @NonNull String mUidStatePrefix;
private final @NonNull PrintWriter mWriter;
private final int mFilterUid;
private final String mFilterPackage;
- private final String mFilterFeatureId;
+ private final String mFilterAttributionTag;
private final int mFilterOp;
private final @HistoricalOpsRequestFilter int mFilter;
StringDumpVisitor(@NonNull String prefix, @NonNull PrintWriter writer, int filterUid,
- @Nullable String filterPackage, @Nullable String filterFeatureId, int filterOp,
+ @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp,
@HistoricalOpsRequestFilter int filter) {
mOpsPrefix = prefix + " ";
mUidPrefix = mOpsPrefix + " ";
mPackagePrefix = mUidPrefix + " ";
- mFeaturePrefix = mPackagePrefix + " ";
- mEntryPrefix = mFeaturePrefix + " ";
+ mAttributionPrefix = mPackagePrefix + " ";
+ mEntryPrefix = mAttributionPrefix + " ";
mUidStatePrefix = mEntryPrefix + " ";
mWriter = writer;
mFilterUid = filterUid;
mFilterPackage = filterPackage;
- mFilterFeatureId = filterFeatureId;
+ mFilterAttributionTag = filterAttributionTag;
mFilterOp = filterOp;
mFilter = filter;
}
@@ -1791,14 +1793,14 @@
}
@Override
- public void visitHistoricalFeatureOps(HistoricalFeatureOps ops) {
- if ((mFilter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(mFilterPackage,
- ops.getFeatureId())) {
+ public void visitHistoricalAttributionOps(AppOpsManager.AttributedHistoricalOps ops) {
+ if ((mFilter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !Objects.equals(mFilterPackage,
+ ops.getTag())) {
return;
}
- mWriter.print(mFeaturePrefix);
- mWriter.print("Feature ");
- mWriter.print(ops.getFeatureId());
+ mWriter.print(mAttributionPrefix);
+ mWriter.print("Attribution ");
+ mWriter.print(ops.getTag());
mWriter.println(":");
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index ecdafb0..e7c09ba 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -24,6 +24,7 @@
import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.UserSwitchObserver;
@@ -296,7 +297,7 @@
}
case MSG_ON_DISMISSED: {
- handleOnDismissed(msg.arg1);
+ handleOnDismissed(msg.arg1, (byte[]) msg.obj);
break;
}
@@ -611,8 +612,12 @@
}
@Override
- public void onDialogDismissed(int reason) throws RemoteException {
- mHandler.obtainMessage(MSG_ON_DISMISSED, reason, 0 /* arg2 */).sendToTarget();
+ public void onDialogDismissed(int reason, @Nullable byte[] credentialAttestation)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_DISMISSED,
+ reason,
+ 0 /* arg2 */,
+ credentialAttestation /* obj */).sendToTarget();
}
@Override
@@ -1422,7 +1427,8 @@
0 /* biometricModality */,
false /* requireConfirmation */,
mCurrentAuthSession.mUserId,
- mCurrentAuthSession.mOpPackageName);
+ mCurrentAuthSession.mOpPackageName,
+ mCurrentAuthSession.mSessionId);
} else {
mPendingAuthSession.mClientReceiver.onError(modality, error, vendorCode);
mPendingAuthSession = null;
@@ -1458,7 +1464,7 @@
}
}
- private void handleOnDismissed(int reason) {
+ private void handleOnDismissed(int reason, @Nullable byte[] credentialAttestation) {
if (mCurrentAuthSession == null) {
Slog.e(TAG, "onDismissed: " + reason + ", auth session null");
return;
@@ -1469,6 +1475,7 @@
try {
switch (reason) {
case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
+ mKeyStore.addAuthToken(credentialAttestation);
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
if (mCurrentAuthSession.mTokenEscrow != null) {
@@ -1616,7 +1623,8 @@
try {
mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle,
mInternalReceiver, modality, requireConfirmation, userId,
- mCurrentAuthSession.mOpPackageName);
+ mCurrentAuthSession.mOpPackageName,
+ mCurrentAuthSession.mSessionId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
@@ -1701,7 +1709,8 @@
0 /* biometricModality */,
false /* requireConfirmation */,
mCurrentAuthSession.mUserId,
- mCurrentAuthSession.mOpPackageName);
+ mCurrentAuthSession.mOpPackageName,
+ sessionId);
} else {
mPendingAuthSession.mState = STATE_AUTH_CALLED;
for (AuthenticatorWrapper authenticator : mAuthenticators) {
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 7bdeb59..2e9818d 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -151,6 +151,15 @@
return true;
}
+ /**
+ * Checks whether a change has an override for a package.
+ * @param packageName name of the package
+ * @return true if there is such override
+ */
+ boolean hasOverride(String packageName) {
+ return mPackageOverrides != null && mPackageOverrides.containsKey(packageName);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ChangeId(")
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index b2ea311..aeaa1fe 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -247,11 +247,13 @@
CompatChange c = mChanges.get(changeId);
try {
if (c != null) {
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(changeId, packageName);
- allowedState.enforce(changeId, packageName);
- overrideExists = true;
- c.removePackageOverride(packageName);
+ overrideExists = c.hasOverride(packageName);
+ if (overrideExists) {
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+ allowedState.enforce(changeId, packageName);
+ c.removePackageOverride(packageName);
+ }
}
} catch (RemoteException e) {
// Should never occur, since validator is in the same process.
@@ -298,12 +300,14 @@
for (int i = 0; i < mChanges.size(); ++i) {
try {
CompatChange change = mChanges.valueAt(i);
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(change.getId(),
- packageName);
- allowedState.enforce(change.getId(), packageName);
- if (change != null) {
- mChanges.valueAt(i).removePackageOverride(packageName);
+ if (change.hasOverride(packageName)) {
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(change.getId(),
+ packageName);
+ allowedState.enforce(change.getId(), packageName);
+ if (change != null) {
+ mChanges.valueAt(i).removePackageOverride(packageName);
+ }
}
} catch (RemoteException e) {
// Should never occur, since validator is in the same process.
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 8519b00..44f571a 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -28,6 +28,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
+import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
@@ -44,6 +45,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* System server internal API for gating and reporting compatibility changes.
@@ -56,6 +58,9 @@
private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig;
+ private static int sMinTargetSdk = Build.VERSION_CODES.P;
+ private static int sMaxTargetSdk = Build.VERSION_CODES.Q;
+
public PlatformCompat(Context context) {
mContext = context;
mChangeReporter = new ChangeReporter(
@@ -220,6 +225,12 @@
return mCompatConfig.dumpChanges();
}
+ @Override
+ public CompatibilityChangeInfo[] listUIChanges() {
+ return Arrays.stream(listAllChanges()).filter(
+ x -> isShownInUI(x)).toArray(CompatibilityChangeInfo[]::new);
+ }
+
/**
* Check whether the change is known to the compat config.
*
@@ -339,4 +350,17 @@
checkCompatChangeReadPermission();
checkCompatChangeLogPermission();
}
+
+ private boolean isShownInUI(CompatibilityChangeInfo change) {
+ if (change.getLoggingOnly()) {
+ return false;
+ }
+ if (change.getEnableAfterTargetSdk() != 0) {
+ if (change.getEnableAfterTargetSdk() < sMinTargetSdk
+ || change.getEnableAfterTargetSdk() > sMaxTargetSdk) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7c3cab1..20ffd9f 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2563,7 +2563,7 @@
public void exitIfOuterInterfaceIs(String interfaze) {
if (interfaze.equals(mOuterInterface)) {
Log.i(TAG, "Legacy VPN is going down with " + interfaze);
- exit();
+ exitVpnRunner();
}
}
@@ -2572,6 +2572,10 @@
public void exitVpnRunner() {
// We assume that everything is reset after stopping the daemons.
interrupt();
+
+ // Always disconnect. This may be called again in cleanupVpnStateLocked() if
+ // exitVpnRunner() was called from exit(), but it will be a no-op.
+ agentDisconnect();
try {
mContext.unregisterReceiver(mBroadcastReceiver);
} catch (IllegalArgumentException e) {}
@@ -2794,7 +2798,7 @@
} catch (Exception e) {
Log.i(TAG, "Aborting", e);
updateState(DetailedState.FAILED, e.getMessage());
- exit();
+ exitVpnRunner();
}
}
diff --git a/services/core/java/com/android/server/hdmi/TEST_MAPPING b/services/core/java/com/android/server/hdmi/TEST_MAPPING
new file mode 100644
index 0000000..7245ec4
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/TEST_MAPPING
@@ -0,0 +1,34 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.hdmi"
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.hdmi"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 6da0de1..44ab438 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -50,6 +50,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -69,8 +70,11 @@
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
@@ -85,6 +89,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/** Implementation of {@link AppIntegrityManagerService}. */
public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
@@ -239,6 +245,11 @@
return new ParceledListSlice<>(rules);
}
+ @Override
+ public List<String> getWhitelistedRuleProviders() throws RemoteException {
+ return getAllowedRuleProviders();
+ }
+
private void handleIntegrityVerification(Intent intent) {
int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
@@ -467,8 +478,23 @@
if (installationPath == null) {
throw new IllegalArgumentException("Installation path is null, package not found");
}
- SourceStampVerificationResult sourceStampVerificationResult =
- SourceStampVerifier.verify(installationPath.getAbsolutePath());
+
+ SourceStampVerificationResult sourceStampVerificationResult;
+ if (installationPath.isDirectory()) {
+ try (Stream<Path> filesList = Files.list(installationPath.toPath())) {
+ List<String> apkFiles =
+ filesList
+ .map(path -> path.toAbsolutePath().toString())
+ .collect(Collectors.toList());
+ sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Could not read APK directory");
+ }
+ } else {
+ sourceStampVerificationResult =
+ SourceStampVerifier.verify(installationPath.getAbsolutePath());
+ }
+
appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent());
appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified());
// A verified stamp is set to be trusted.
diff --git a/services/core/java/com/android/server/location/CountryDetectorBase.java b/services/core/java/com/android/server/location/CountryDetectorBase.java
index b158388..682b104 100644
--- a/services/core/java/com/android/server/location/CountryDetectorBase.java
+++ b/services/core/java/com/android/server/location/CountryDetectorBase.java
@@ -31,7 +31,7 @@
* @hide
*/
public abstract class CountryDetectorBase {
- private static final String FEATURE_ID = "CountryDetector";
+ private static final String ATTRIBUTION_TAG = "CountryDetector";
protected final Handler mHandler;
protected final Context mContext;
@@ -39,7 +39,7 @@
protected Country mDetectedCountry;
public CountryDetectorBase(Context context) {
- mContext = context.createFeatureContext(FEATURE_ID);
+ mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mHandler = new Handler();
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c1c3760..1b4ec8a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -530,6 +530,11 @@
return Settings.Global.getInt(contentResolver, keyName, defaultValue);
}
+ public int settingsSecureGetInt(ContentResolver contentResolver, String keyName,
+ int defaultValue, int userId) {
+ return Settings.Secure.getIntForUser(contentResolver, keyName, defaultValue, userId);
+ }
+
public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache() {
try {
java.security.KeyStore ks = java.security.KeyStore.getInstance("AndroidKeyStore");
@@ -1010,6 +1015,13 @@
}
}
+ private void enforceFrpResolved() {
+ if (mInjector.settingsSecureGetInt(mContext.getContentResolver(),
+ Settings.Secure.SECURE_FRP_MODE, 0, UserHandle.USER_SYSTEM) == 1) {
+ throw new SecurityException("Cannot change credential while FRP is not resolved yet");
+ }
+ }
+
private final void checkWritePermission(int userId) {
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
}
@@ -1572,6 +1584,7 @@
"This operation requires secure lock screen feature");
}
checkWritePermission(userId);
+ enforceFrpResolved();
// When changing credential for profiles with unified challenge, some callers
// will pass in empty credential while others will pass in the credential of
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 265b9ad..28f8380 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -85,7 +85,9 @@
mListener = listener;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
buildBluetoothRoutes();
+ }
+ public void start() {
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index aad7203..c7d14e0 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -103,32 +103,29 @@
publishProviderState();
boolean sessionInfoChanged;
- synchronized (mLock) {
- sessionInfoChanged = updateSessionInfosIfNeededLocked();
- }
+ sessionInfoChanged = updateSessionInfosIfNeeded();
if (sessionInfoChanged) {
notifySessionInfoUpdated();
}
});
-
- mHandler.post(() -> notifyProviderState());
-
- //TODO: clean up this
- // This is required because it is not instantiated in the main thread and
- // BluetoothRoutesUpdatedListener can be called before here
- synchronized (mLock) {
- updateSessionInfosIfNeededLocked();
- }
+ updateSessionInfosIfNeeded();
mContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
+
+ mHandler.post(() -> {
+ mBtRouteProvider.start();
+ notifyProviderState();
+ });
}
@Override
public void requestCreateSession(long requestId, String packageName, String routeId,
Bundle sessionHints) {
- // Handle it as an internal transfer.
+
transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
+ mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
+ //TODO: We should call after the session info is changed.
}
@Override
@@ -218,36 +215,38 @@
/**
* Updates the mSessionInfo. Returns true if the session info is changed.
*/
- boolean updateSessionInfosIfNeededLocked() {
- // Prevent to execute this method before mBtRouteProvider is created.
- if (mBtRouteProvider == null) return false;
- RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
+ boolean updateSessionInfosIfNeeded() {
+ synchronized (mLock) {
+ // Prevent to execute this method before mBtRouteProvider is created.
+ if (mBtRouteProvider == null) return false;
+ RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
+ 0);
- RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
- SYSTEM_SESSION_ID, "" /* clientPackageName */)
- .setSystemSession(true);
+ RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
+ SYSTEM_SESSION_ID, "" /* clientPackageName */)
+ .setSystemSession(true);
- MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
- if (selectedRoute == null) {
- selectedRoute = mDefaultRoute;
- } else {
- builder.addTransferableRoute(mDefaultRoute.getId());
- }
- mSelectedRouteId = selectedRoute.getId();
- builder.addSelectedRoute(mSelectedRouteId);
+ MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
+ if (selectedRoute == null) {
+ selectedRoute = mDefaultRoute;
+ } else {
+ builder.addTransferableRoute(mDefaultRoute.getId());
+ }
+ mSelectedRouteId = selectedRoute.getId();
+ builder.addSelectedRoute(mSelectedRouteId);
- for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
- builder.addTransferableRoute(route.getId());
- }
+ for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
+ builder.addTransferableRoute(route.getId());
+ }
-
- RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
- if (Objects.equals(oldSessionInfo, newSessionInfo)) {
- return false;
- } else {
- mSessionInfos.clear();
- mSessionInfos.add(newSessionInfo);
- return true;
+ RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
+ if (Objects.equals(oldSessionInfo, newSessionInfo)) {
+ return false;
+ } else {
+ mSessionInfos.clear();
+ mSessionInfos.add(newSessionInfo);
+ return true;
+ }
}
}
@@ -261,6 +260,7 @@
synchronized (mLock) {
sessionInfo = mSessionInfos.get(0);
}
+
mCallback.onSessionUpdated(this, sessionInfo);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 563dcf7..48f1ddb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -21,7 +21,7 @@
import android.annotation.NonNull;
import android.net.Network;
import android.net.NetworkTemplate;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.telephony.SubscriptionPlan;
import java.util.Set;
@@ -130,8 +130,8 @@
Set<String> packageNames, int userId);
/**
- * Notifies that the specified {@link AbstractNetworkStatsProvider} has reached its quota
- * which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}.
+ * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+ * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)}.
*
* @param tag the human readable identifier of the custom network stats provider.
*/
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0b1c91f..caf2914 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -75,7 +75,7 @@
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.net.netstats.provider.AbstractNetworkStatsProvider.QUOTA_UNLIMITED;
+import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED;
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 22b01be..75ffe35 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -229,7 +229,7 @@
entry.txPackets += reader.nextLong();
}
- stats.addEntry(entry);
+ stats.insertEntry(entry);
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
@@ -279,7 +279,7 @@
entry.txBytes = reader.nextLong();
entry.txPackets = reader.nextLong();
- stats.addEntry(entry);
+ stats.insertEntry(entry);
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
@@ -439,7 +439,7 @@
if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
&& (limitUid == UID_ALL || limitUid == entry.uid)
&& (limitTag == TAG_ALL || limitTag == entry.tag)) {
- stats.addEntry(entry);
+ stats.insertEntry(entry);
}
reader.finishLine();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4af31b0..10136b3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -17,12 +17,14 @@
package com.android.server.net;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.ConnectivityManager.isNetworkTypeMobile;
import static android.net.NetworkStack.checkNetworkStackPermission;
@@ -101,7 +103,7 @@
import android.net.TrafficStats;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.os.BestClock;
import android.os.Binder;
import android.os.DropBoxManager;
@@ -556,7 +558,7 @@
} catch (RemoteException e) {
// ignored; service lives in system_server
}
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes));
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetAlert(mGlobalAlertBytes));
}
@Override
@@ -757,7 +759,7 @@
final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
final NetworkStats stats = new NetworkStats(end - start, 1);
- stats.addEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
+ stats.insertEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets,
entry.txBytes, entry.txPackets, entry.operations));
return stats;
@@ -1374,7 +1376,8 @@
Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
final int registeredCallbackCount = mStatsProviderCbList.getRegisteredCallbackCount();
mStatsProviderSem.drainPermits();
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
+ invokeForAllStatsProviderCallbacks(
+ (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
try {
mStatsProviderSem.tryAcquire(registeredCallbackCount,
MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
@@ -1549,7 +1552,7 @@
@Override
public void setStatsProviderLimitAsync(@NonNull String iface, long quota) {
Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")");
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota));
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetLimit(iface, quota));
}
}
@@ -1793,6 +1796,24 @@
}
}
+ // TODO: It is copied from ConnectivityService, consider refactor these check permission
+ // functions to a proper util.
+ private boolean checkAnyPermissionOf(String... permissions) {
+ for (String permission : permissions) {
+ if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void enforceAnyPermissionOf(String... permissions) {
+ if (!checkAnyPermissionOf(permissions)) {
+ throw new SecurityException("Requires one of the following permissions: "
+ + String.join(", ", permissions) + ".");
+ }
+ }
+
/**
* Registers a custom provider of {@link android.net.NetworkStats} to combine the network
* statistics that cannot be seen by the kernel to system. To unregister, invoke the
@@ -1800,16 +1821,15 @@
*
* @param tag a human readable identifier of the custom network stats provider.
* @param provider the {@link INetworkStatsProvider} binder corresponding to the
- * {@link android.net.netstats.provider.AbstractNetworkStatsProvider} to be
- * registered.
+ * {@link NetworkStatsProvider} to be registered.
*
- * @return a binder interface of
- * {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be
- * used to report events to the system.
+ * @return a {@link INetworkStatsProviderCallback} binder
+ * interface, which can be used to report events to the system.
*/
public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
@NonNull String tag, @NonNull INetworkStatsProvider provider) {
- mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+ enforceAnyPermissionOf(NETWORK_STATS_PROVIDER,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
Objects.requireNonNull(provider, "provider is null");
Objects.requireNonNull(tag, "tag is null");
try {
@@ -1910,7 +1930,7 @@
}
@Override
- public void onStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
+ public void notifyStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
@Nullable NetworkStats uidStats) {
// TODO: 1. Use token to map ifaces to correct NetworkIdentity.
// 2. Store the difference and store it directly to the recorder.
@@ -1922,12 +1942,12 @@
}
@Override
- public void onAlertReached() throws RemoteException {
+ public void notifyAlertReached() throws RemoteException {
mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
}
@Override
- public void onLimitReached() {
+ public void notifyLimitReached() {
Log.d(TAG, mTag + ": onLimitReached");
LocalServices.getService(NetworkPolicyManagerInternal.class)
.onStatsProviderLimitReached(mTag);
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index c9c8042..c96880c 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -15,9 +15,27 @@
*/
package com.android.server.notification;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+
+import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
+import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Person;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayList;
+
/**
* Determines whether a bubble can be shown for this notification
*/
@@ -25,10 +43,15 @@
private static final String TAG = "BubbleExtractor";
private static final boolean DBG = false;
+ private BubbleChecker mBubbleChecker;
private RankingConfig mConfig;
+ private ActivityManager mActivityManager;
+ private Context mContext;
- public void initialize(Context ctx, NotificationUsageStats usageStats) {
+ public void initialize(Context context, NotificationUsageStats usageStats) {
if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ mContext = context;
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
public RankingReconsideration process(NotificationRecord record) {
@@ -41,6 +64,12 @@
if (DBG) Slog.d(TAG, "missing config");
return null;
}
+
+ if (mBubbleChecker == null) {
+ if (DBG) Slog.d(TAG, "missing bubble checker");
+ return null;
+ }
+
boolean appCanShowBubble =
mConfig.areBubblesAllowed(record.getSbn().getPackageName(), record.getSbn().getUid());
if (!mConfig.bubblesEnabled() || !appCanShowBubble) {
@@ -52,7 +81,12 @@
record.setAllowBubble(appCanShowBubble);
}
}
-
+ final boolean applyFlag = mBubbleChecker.isNotificationAppropriateToBubble(record);
+ if (applyFlag) {
+ record.getNotification().flags |= FLAG_BUBBLE;
+ } else {
+ record.getNotification().flags &= ~FLAG_BUBBLE;
+ }
return null;
}
@@ -64,4 +98,185 @@
@Override
public void setZenHelper(ZenModeHelper helper) {
}
+
+ /**
+ * Expected to be called after {@link #setConfig(RankingConfig)} has occurred.
+ */
+ void setShortcutHelper(ShortcutHelper helper) {
+ if (mConfig == null) {
+ if (DBG) Slog.d(TAG, "setting shortcut helper prior to setConfig");
+ return;
+ }
+ mBubbleChecker = new BubbleChecker(mContext, helper, mConfig, mActivityManager);
+ }
+
+ @VisibleForTesting
+ void setBubbleChecker(BubbleChecker checker) {
+ mBubbleChecker = checker;
+ }
+
+ /**
+ * Encapsulates special checks to see if a notification can be flagged as a bubble. This
+ * makes testing a bit easier.
+ */
+ public static class BubbleChecker {
+
+ private ActivityManager mActivityManager;
+ private RankingConfig mRankingConfig;
+ private Context mContext;
+ private ShortcutHelper mShortcutHelper;
+
+ BubbleChecker(Context context, ShortcutHelper helper, RankingConfig config,
+ ActivityManager activityManager) {
+ mContext = context;
+ mActivityManager = activityManager;
+ mShortcutHelper = helper;
+ mRankingConfig = config;
+ }
+
+ /**
+ * @return whether the provided notification record is allowed to be represented as a
+ * bubble, accounting for user choice & policy.
+ */
+ public boolean isNotificationAppropriateToBubble(NotificationRecord r) {
+ final String pkg = r.getSbn().getPackageName();
+ final int userId = r.getSbn().getUser().getIdentifier();
+ Notification notification = r.getNotification();
+ if (!canBubble(r, pkg, userId)) {
+ // no log: canBubble has its own
+ return false;
+ }
+
+ if (mActivityManager.isLowRamDevice()) {
+ logBubbleError(r.getKey(), "low ram device");
+ return false;
+ }
+
+ // At this point the bubble must fulfill communication policy
+
+ // Communication always needs a person
+ ArrayList<Person> peopleList = notification.extras != null
+ ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
+ : null;
+ // Message style requires a person & it's not included in the list
+ boolean isMessageStyle = Notification.MessagingStyle.class.equals(
+ notification.getNotificationStyle());
+ if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
+ logBubbleError(r.getKey(), "Must have a person and be "
+ + "Notification.MessageStyle or Notification.CATEGORY_CALL");
+ return false;
+ }
+
+ // Communication is a message or a call
+ boolean isCall = CATEGORY_CALL.equals(notification.category);
+ boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
+ if (hasForegroundService && !isCall) {
+ logBubbleError(r.getKey(),
+ "foreground services must be Notification.CATEGORY_CALL to bubble");
+ return false;
+ }
+ if (isMessageStyle) {
+ return true;
+ } else if (isCall) {
+ if (hasForegroundService) {
+ return true;
+ }
+ logBubbleError(r.getKey(), "calls require foreground service");
+ return false;
+ }
+ logBubbleError(r.getKey(), "Must be "
+ + "Notification.MessageStyle or Notification.CATEGORY_CALL");
+ return false;
+ }
+
+ /**
+ * @return whether the user has enabled the provided notification to bubble, does not
+ * account for policy.
+ */
+ @VisibleForTesting
+ boolean canBubble(NotificationRecord r, String pkg, int userId) {
+ Notification notification = r.getNotification();
+ Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
+ if (metadata == null) {
+ // no log: no need to inform dev if they didn't attach bubble metadata
+ return false;
+ }
+ if (!mRankingConfig.bubblesEnabled()) {
+ logBubbleError(r.getKey(), "bubbles disabled for user: " + userId);
+ return false;
+ }
+ if (!mRankingConfig.areBubblesAllowed(pkg, userId)) {
+ logBubbleError(r.getKey(),
+ "bubbles for package: " + pkg + " disabled for user: " + userId);
+ return false;
+ }
+ if (!r.getChannel().canBubble()) {
+ logBubbleError(r.getKey(),
+ "bubbles for channel " + r.getChannel().getId() + " disabled");
+ return false;
+ }
+
+ String shortcutId = metadata.getShortcutId();
+ boolean shortcutValid = shortcutId != null
+ && mShortcutHelper.hasValidShortcutInfo(shortcutId, pkg, r.getUser());
+ if (metadata.getBubbleIntent() == null && !shortcutValid) {
+ // Should have a shortcut if intent is null
+ logBubbleError(r.getKey(),
+ "couldn't find valid shortcut for bubble with shortcutId: " + shortcutId);
+ return false;
+ }
+ if (shortcutValid) {
+ return true;
+ }
+ // no log: canLaunch method has the failure log
+ return canLaunchInActivityView(mContext, metadata.getBubbleIntent(), pkg);
+ }
+
+ /**
+ * Whether an intent is properly configured to display in an {@link
+ * android.app.ActivityView}.
+ *
+ * @param context the context to use.
+ * @param pendingIntent the pending intent of the bubble.
+ * @param packageName the notification package name for this bubble.
+ */
+ // Keep checks in sync with BubbleController#canLaunchInActivityView.
+ @VisibleForTesting
+ protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
+ String packageName) {
+ if (pendingIntent == null) {
+ Slog.w(TAG, "Unable to create bubble -- no intent");
+ return false;
+ }
+
+ Intent intent = pendingIntent.getIntent();
+
+ ActivityInfo info = intent != null
+ ? intent.resolveActivityInfo(context.getPackageManager(), 0)
+ : null;
+ if (info == null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
+ packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+ Slog.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
+ + intent);
+ return false;
+ }
+ if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
+ packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+ Slog.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
+ + intent);
+ return false;
+ }
+ return true;
+ }
+
+ private void logBubbleError(String key, String failureMessage) {
+ if (DBG) {
+ Slog.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 69a5b35..20ad87a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.notification;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
-import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -51,9 +50,6 @@
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_NOT_PERCEPTIBLE;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -93,8 +89,6 @@
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
-import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -131,8 +125,6 @@
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteInput;
import android.app.StatsManager;
import android.app.StatusBarManager;
import android.app.UriGrantsManager;
@@ -152,7 +144,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
@@ -160,7 +151,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
-import android.content.pm.ShortcutInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -251,7 +241,6 @@
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
@@ -296,7 +285,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
@@ -421,7 +409,7 @@
private RoleObserver mRoleObserver;
private UserManager mUm;
private IPlatformCompat mPlatformCompat;
- private LauncherApps mLauncherAppsService;
+ private ShortcutHelper mShortcutHelper;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -497,7 +485,8 @@
"allow-secure-notifications-on-lockscreen";
private static final String LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE = "value";
- private RankingHelper mRankingHelper;
+ @VisibleForTesting
+ RankingHelper mRankingHelper;
@VisibleForTesting
PreferencesHelper mPreferencesHelper;
@@ -931,6 +920,8 @@
.setType(MetricsEvent.TYPE_ACTION)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED, r);
EventLogTags.writeNotificationClicked(key,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
nv.rank, nv.count);
@@ -970,7 +961,8 @@
generatedByAssistant ? 1 : 0)
.addTaggedData(MetricsEvent.NOTIFICATION_LOCATION,
nv.location.toMetricsEventEnum()));
-
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_ACTION_CLICKED, r);
EventLogTags.writeNotificationActionClicked(key, actionIndex,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
nv.rank, nv.count);
@@ -1004,6 +996,8 @@
public void onPanelRevealed(boolean clearEffects, int items) {
MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
MetricsLogger.histogram(getContext(), "note_load", items);
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_OPEN);
EventLogTags.writeNotificationPanelRevealed(items);
if (clearEffects) {
clearEffects();
@@ -1014,6 +1008,8 @@
@Override
public void onPanelHidden() {
MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_CLOSE);
EventLogTags.writeNotificationPanelHidden();
mAssistants.onPanelHidden();
}
@@ -1103,6 +1099,10 @@
MetricsLogger.action(r.getItemLogMaker()
.setType(expanded ? MetricsEvent.TYPE_DETAIL
: MetricsEvent.TYPE_COLLAPSE));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.fromExpanded(expanded,
+ userAction),
+ r);
}
if (expanded && userAction) {
r.recordExpanded();
@@ -1124,6 +1124,9 @@
mMetricsLogger.write(r.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
.setType(MetricsEvent.TYPE_ACTION));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
+ r);
reportUserInteraction(r);
mAssistants.notifyAssistantNotificationDirectReplyLocked(r.getSbn());
}
@@ -1166,6 +1169,9 @@
MetricsEvent.NOTIFICATION_SMART_REPLY_MODIFIED_BEFORE_SENDING,
modifiedBeforeSending ? 1 : 0);
mMetricsLogger.write(logMaker);
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
+ r);
// Treat clicking on a smart reply as a user interaction.
reportUserInteraction(r);
mAssistants.notifyAssistantSuggestedReplySent(
@@ -1186,13 +1192,30 @@
@Override
public void onNotificationBubbleChanged(String key, boolean isBubble) {
+ String pkg;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
+ }
+ boolean isAppForeground = pkg != null
+ && mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
- final StatusBarNotification n = r.getSbn();
- final int callingUid = n.getUid();
- final String pkg = n.getPackageName();
- applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isBubble);
+ if (!isBubble) {
+ // This happens if the user has dismissed the bubble but the notification
+ // is still active in the shade, enqueuing would create a bubble since
+ // the notification is technically allowed. Flip the flag so that
+ // apps querying noMan will know that their notification is not showing
+ // as a bubble.
+ r.getNotification().flags &= ~FLAG_BUBBLE;
+ } else {
+ // Enqueue will trigger resort & if the flag is allowed to be true it'll
+ // be applied there.
+ r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+ mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(),
+ r, isAppForeground));
+ }
}
}
}
@@ -1219,6 +1242,7 @@
flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
}
data.setFlags(flags);
+ r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r,
true /* isAppForeground */));
}
@@ -1310,6 +1334,9 @@
MetricsEvent.NOTIFICATION_SMART_REPLY_EDIT_BEFORE_SENDING,
r.getEditChoicesBeforeSending() ? 1 : 0);
mMetricsLogger.write(logMaker);
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLY_VISIBLE,
+ r);
}
}
@@ -1595,80 +1622,6 @@
}
};
- // Key: packageName Value: <shortcutId, notifId>
- private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
-
- private boolean mLauncherAppsCallbackRegistered;
-
- // Bubbles can be created based on a shortcut, we need to listen for changes to
- // that shortcut so that we may update the bubble appropriately.
- private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() {
- @Override
- public void onPackageRemoved(String packageName, UserHandle user) {
- }
-
- @Override
- public void onPackageAdded(String packageName, UserHandle user) {
- }
-
- @Override
- public void onPackageChanged(String packageName, UserHandle user) {
- }
-
- @Override
- public void onPackagesAvailable(String[] packageNames, UserHandle user,
- boolean replacing) {
- }
-
- @Override
- public void onPackagesUnavailable(String[] packageNames, UserHandle user,
- boolean replacing) {
- }
-
- @Override
- public void onShortcutsChanged(@NonNull String packageName,
- @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
- HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
- boolean isAppForeground = packageName != null
- && mActivityManager.getPackageImportance(packageName) == IMPORTANCE_FOREGROUND;
- ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
- if (shortcutBubbles != null) {
- // If we can't find one of our bubbles in the shortcut list, that bubble needs
- // to be removed.
- for (String shortcutId : shortcutBubbles.keySet()) {
- boolean foundShortcut = false;
- for (int i = 0; i < shortcuts.size(); i++) {
- if (shortcuts.get(i).getId().equals(shortcutId)) {
- foundShortcut = true;
- break;
- }
- }
- if (!foundShortcut) {
- bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId));
- }
- }
- }
-
- // Do the removals
- for (int i = 0; i < bubbleKeysToRemove.size(); i++) {
- // update flag bubble
- String bubbleKey = bubbleKeysToRemove.get(i);
- synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(bubbleKey);
- if (r != null) {
- final StatusBarNotification n = r.getSbn();
- final int callingUid = n.getUid();
- final String pkg = n.getPackageName();
- applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isAppForeground);
- mHandler.post(new EnqueueNotificationRunnable(user.getIdentifier(), r,
- false /* isAppForeground */));
- }
- }
- }
- }
- };
-
-
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_BADGING_URI
= Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
@@ -1763,8 +1716,8 @@
}
@VisibleForTesting
- void setLauncherApps(LauncherApps launcherApps) {
- mLauncherAppsService = launcherApps;
+ ShortcutHelper getShortcutHelper() {
+ return mShortcutHelper;
}
@VisibleForTesting
@@ -2314,8 +2267,13 @@
mRoleObserver = new RoleObserver(getContext().getSystemService(RoleManager.class),
mPackageManager, getContext().getMainExecutor());
mRoleObserver.init();
- mLauncherAppsService =
+ LauncherApps launcherApps =
(LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
+ mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener);
+ BubbleExtractor bubbsExtractor = mRankingHelper.findExtractor(BubbleExtractor.class);
+ if (bubbsExtractor != null) {
+ bubbsExtractor.setShortcutHelper(mShortcutHelper);
+ }
registerNotificationPreferencesPullers();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
@@ -3458,7 +3416,7 @@
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(onlyImportant);
for (ConversationChannelWrapper conversation : conversations) {
- conversation.setShortcutInfo(getShortcutInfo(
+ conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
conversation.getNotificationChannel().getConversationId(),
conversation.getPkg(),
UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
@@ -3481,7 +3439,7 @@
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(pkg, uid);
for (ConversationChannelWrapper conversation : conversations) {
- conversation.setShortcutInfo(getShortcutInfo(
+ conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
conversation.getNotificationChannel().getConversationId(),
pkg,
UserHandle.of(UserHandle.getUserId(uid))));
@@ -5652,7 +5610,7 @@
}
}
- r.setShortcutInfo(getShortcutInfo(notification.getShortcutId(), pkg, user));
+ r.setShortcutInfo(mShortcutHelper.getShortcutInfo(notification.getShortcutId(), pkg, user));
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.getSbn().getOverrideGroupKey() != null)) {
@@ -5780,16 +5738,12 @@
}
/**
- * Updates the flags for this notification to reflect whether it is a bubble or not. Some
- * bubble specific flags only work if the app is foreground, this will strip those flags
+ * Some bubble specific flags only work if the app is foreground, this will strip those flags
* if the app wasn't foreground.
*/
- private void updateNotificationBubbleFlags(NotificationRecord r, String pkg, int userId,
- NotificationRecord oldRecord, boolean isAppForeground) {
- Notification notification = r.getNotification();
- applyFlagBubble(r, pkg, userId, oldRecord, true /* desiredFlag */);
-
+ private void updateNotificationBubbleFlags(NotificationRecord r, boolean isAppForeground) {
// Remove any bubble specific flags that only work when foregrounded
+ Notification notification = r.getNotification();
Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
if (!isAppForeground && metadata != null) {
int flags = metadata.getFlags();
@@ -5799,252 +5753,30 @@
}
}
- /**
- * Handles actually applying or removing {@link Notification#FLAG_BUBBLE}. Performs necessary
- * checks for the provided record to see if it can actually be a bubble.
- * Tracks shortcut based bubbles so that we can find out if they've changed or been removed.
- */
- private void applyFlagBubble(NotificationRecord r, String pkg, int userId,
- NotificationRecord oldRecord, boolean desiredFlag) {
- boolean applyFlag = desiredFlag
- && isNotificationAppropriateToBubble(r, pkg, userId, oldRecord);
- final String shortcutId = r.getNotification().getBubbleMetadata() != null
- ? r.getNotification().getBubbleMetadata().getShortcutId()
- : null;
- if (applyFlag) {
- if (shortcutId != null) {
- // Must track shortcut based bubbles in case the shortcut is removed
- HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
- r.getSbn().getPackageName());
- if (packageBubbles == null) {
- packageBubbles = new HashMap<>();
+ private ShortcutHelper.ShortcutListener mShortcutListener =
+ new ShortcutHelper.ShortcutListener() {
+ @Override
+ public void onShortcutRemoved(String key) {
+ String packageName;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ packageName = r != null ? r.getSbn().getPackageName() : null;
+ }
+ boolean isAppForeground = packageName != null
+ && mActivityManager.getPackageImportance(packageName)
+ == IMPORTANCE_FOREGROUND;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ // Enqueue will trigger resort & flag is updated that way.
+ r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+ mHandler.post(
+ new NotificationManagerService.EnqueueNotificationRunnable(
+ r.getUser().getIdentifier(), r, isAppForeground));
+ }
+ }
}
- packageBubbles.put(shortcutId, r.getKey());
- mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
- if (!mLauncherAppsCallbackRegistered) {
- mLauncherAppsService.registerCallback(mLauncherAppsCallback, mHandler);
- mLauncherAppsCallbackRegistered = true;
- }
- }
- r.getNotification().flags |= FLAG_BUBBLE;
- } else {
- if (shortcutId != null) {
- // No longer track shortcut
- HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
- r.getSbn().getPackageName());
- if (packageBubbles != null) {
- packageBubbles.remove(shortcutId);
- }
- if (packageBubbles != null && packageBubbles.isEmpty()) {
- mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
- }
- if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
- mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
- mLauncherAppsCallbackRegistered = false;
- }
- }
- r.getNotification().flags &= ~FLAG_BUBBLE;
- }
- }
-
- /**
- * @return whether the provided notification record is allowed to be represented as a bubble,
- * accounting for user choice & policy.
- */
- private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId,
- NotificationRecord oldRecord) {
- Notification notification = r.getNotification();
- if (!canBubble(r, pkg, userId)) {
- // no log: canBubble has its own
- return false;
- }
-
- if (mActivityManager.isLowRamDevice()) {
- logBubbleError(r.getKey(), "low ram device");
- return false;
- }
-
- if (oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0) {
- // This is an update to an active bubble
- return true;
- }
-
- // At this point the bubble must fulfill communication policy
-
- // Communication always needs a person
- ArrayList<Person> peopleList = notification.extras != null
- ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
- : null;
- // Message style requires a person & it's not included in the list
- boolean isMessageStyle = Notification.MessagingStyle.class.equals(
- notification.getNotificationStyle());
- if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
- logBubbleError(r.getKey(), "Must have a person and be "
- + "Notification.MessageStyle or Notification.CATEGORY_CALL");
- return false;
- }
-
- // Communication is a message or a call
- boolean isCall = CATEGORY_CALL.equals(notification.category);
- boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
- if (hasForegroundService && !isCall) {
- logBubbleError(r.getKey(),
- "foreground services must be Notification.CATEGORY_CALL to bubble");
- return false;
- }
- if (isMessageStyle) {
- if (hasValidRemoteInput(notification)) {
- return true;
- }
- logBubbleError(r.getKey(), "messages require valid remote input");
- return false;
- } else if (isCall) {
- if (hasForegroundService) {
- return true;
- }
- logBubbleError(r.getKey(), "calls require foreground service");
- return false;
- }
- logBubbleError(r.getKey(), "Must be "
- + "Notification.MessageStyle or Notification.CATEGORY_CALL");
- return false;
- }
-
- /**
- * @return whether the user has enabled the provided notification to bubble, does not account
- * for policy.
- */
- private boolean canBubble(NotificationRecord r, String pkg, int userId) {
- Notification notification = r.getNotification();
- Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
- if (metadata == null) {
- // no log: no need to inform dev if they didn't attach bubble metadata
- return false;
- }
- if (!mPreferencesHelper.bubblesEnabled()) {
- logBubbleError(r.getKey(), "bubbles disabled for user: " + userId);
- return false;
- }
- if (!mPreferencesHelper.areBubblesAllowed(pkg, userId)) {
- logBubbleError(r.getKey(),
- "bubbles for package: " + pkg + " disabled for user: " + userId);
- return false;
- }
- if (!r.getChannel().canBubble()) {
- logBubbleError(r.getKey(),
- "bubbles for channel " + r.getChannel().getId() + " disabled");
- return false;
- }
-
- String shortcutId = metadata.getShortcutId();
- boolean shortcutValid = shortcutId != null
- && hasValidShortcutInfo(shortcutId, pkg, r.getUser());
- if (metadata.getBubbleIntent() == null && !shortcutValid) {
- // Should have a shortcut if intent is null
- logBubbleError(r.getKey(), "couldn't find shortcutId for bubble: " + shortcutId);
- return false;
- }
- if (shortcutValid) {
- return true;
- }
- // no log: canLaunch method has the failure log
- return canLaunchInActivityView(getContext(), metadata.getBubbleIntent(), pkg);
- }
-
- private boolean hasValidRemoteInput(Notification n) {
- // Also check for inline reply
- Notification.Action[] actions = n.actions;
- if (actions != null) {
- // Get the remote inputs
- for (int i = 0; i < actions.length; i++) {
- Notification.Action action = actions[i];
- RemoteInput[] inputs = action.getRemoteInputs();
- if (inputs != null && inputs.length > 0) {
- return true;
- }
- }
- }
- return false;
- }
-
- private ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
- final long token = Binder.clearCallingIdentity();
- try {
- if (shortcutId == null || packageName == null || user == null) {
- return null;
- }
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
- if (packageName != null) {
- query.setPackage(packageName);
- }
- if (shortcutId != null) {
- query.setShortcutIds(Arrays.asList(shortcutId));
- }
- query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
- ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
- ? shortcuts.get(0)
- : null;
- return shortcutInfo;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
- ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
- return shortcutInfo != null && shortcutInfo.isLongLived();
- }
-
- private void logBubbleError(String key, String failureMessage) {
- if (DBG) {
- Log.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage);
- }
- }
- /**
- * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
- *
- * @param context the context to use.
- * @param pendingIntent the pending intent of the bubble.
- * @param packageName the notification package name for this bubble.
- */
- // Keep checks in sync with BubbleController#canLaunchInActivityView.
- @VisibleForTesting
- protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
- String packageName) {
- if (pendingIntent == null) {
- Log.w(TAG, "Unable to create bubble -- no intent");
- return false;
- }
-
- // Need escalated privileges to get the intent.
- final long token = Binder.clearCallingIdentity();
- Intent intent;
- try {
- intent = pendingIntent.getIntent();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- ActivityInfo info = intent != null
- ? intent.resolveActivityInfo(context.getPackageManager(), 0)
- : null;
- if (info == null) {
- FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
- Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
- + intent);
- return false;
- }
- if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
- FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
- Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
- + intent);
- return false;
- }
- return true;
- }
+ };
private void doChannelWarningToast(CharSequence toastText) {
Binder.withCleanCallingIdentity(() -> {
@@ -6150,6 +5882,9 @@
MetricsLogger.action(r.getLogMaker()
.setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
+ r);
if (DBG) {
Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
}
@@ -6279,6 +6014,8 @@
mDuration)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED, r);
reportUserInteraction(r);
boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
@@ -6406,6 +6143,8 @@
cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName,
mSendDelete, childrenFlagChecker);
updateLightsLocked();
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(r, true /* isRemoved */,
+ mHandler);
} else {
// No notification was found, assume that it is snoozed and cancel it.
if (mReason != REASON_SNOOZED) {
@@ -6473,7 +6212,7 @@
final String tag = n.getTag();
// We need to fix the notification up a little for bubbles
- updateNotificationBubbleFlags(r, pkg, callingUid, old, isAppForeground);
+ updateNotificationBubbleFlags(r, isAppForeground);
// Handle grouped notifications and bail out early if we
// can to avoid extracting signals.
@@ -6643,6 +6382,10 @@
+ n.getPackageName());
}
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(r,
+ false /* isRemoved */,
+ mHandler);
+
maybeRecordInterruptionLocked(r);
// Log event to statsd
@@ -7402,6 +7145,7 @@
int[] visibilities = new int[N];
boolean[] showBadges = new boolean[N];
boolean[] allowBubbles = new boolean[N];
+ boolean[] isBubble = new boolean[N];
ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
ArrayList<String> groupKeyBefore = new ArrayList<>(N);
ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
@@ -7417,6 +7161,7 @@
visibilities[i] = r.getPackageVisibilityOverride();
showBadges[i] = r.canShowBadge();
allowBubbles[i] = r.canBubble();
+ isBubble[i] = r.getNotification().isBubbleNotification();
channelBefore.add(r.getChannel());
groupKeyBefore.add(r.getGroupKey());
overridePeopleBefore.add(r.getPeopleOverride());
@@ -7435,6 +7180,7 @@
|| visibilities[i] != r.getPackageVisibilityOverride()
|| showBadges[i] != r.canShowBadge()
|| allowBubbles[i] != r.canBubble()
+ || isBubble[i] != r.getNotification().isBubbleNotification()
|| !Objects.equals(channelBefore.get(i), r.getChannel())
|| !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
|| !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
@@ -8597,7 +8343,8 @@
record.canBubble(),
record.isInterruptive(),
record.isConversation(),
- record.getShortcutInfo()
+ record.getShortcutInfo(),
+ record.getNotification().isBubbleNotification()
);
rankings.add(ranking);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index f4ee461..6c833f9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -34,11 +34,14 @@
import java.util.Objects;
/**
- * Interface for writing NotificationReported atoms to statsd log.
+ * Interface for writing NotificationReported atoms to statsd log. Use NotificationRecordLoggerImpl
+ * in production. Use NotificationRecordLoggerFake for testing.
* @hide
*/
public interface NotificationRecordLogger {
+ // The high-level interface used by clients.
+
/**
* May log a NotificationReported atom reflecting the posting or update of a notification.
* @param r The new NotificationRecord. If null, no action is taken.
@@ -57,9 +60,11 @@
* @param reason The reason the notification was canceled.
* @param dismissalSurface The surface the notification was dismissed from.
*/
- void logNotificationCancelled(@Nullable NotificationRecord r,
+ default void logNotificationCancelled(@Nullable NotificationRecord r,
@NotificationListenerService.NotificationCancelReason int reason,
- @NotificationStats.DismissalSurface int dismissalSurface);
+ @NotificationStats.DismissalSurface int dismissalSurface) {
+ log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r);
+ }
/**
* Logs a notification visibility change event using UiEventReported (event ids from the
@@ -67,7 +72,17 @@
* @param r The NotificationRecord. If null, no action is taken.
* @param visible True if the notification became visible.
*/
- void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible);
+ default void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible) {
+ log(NotificationEvent.fromVisibility(visible), r);
+ }
+
+ // The UiEventReported logging methods are implemented in terms of this lower-level interface.
+
+ /** Logs a UiEventReported event for the given notification. */
+ void log(UiEventLogger.UiEventEnum event, NotificationRecord r);
+
+ /** Logs a UiEventReported event that is not associated with any notification. */
+ void log(UiEventLogger.UiEventEnum event);
/**
* The UiEvent enums that this class can log.
@@ -204,7 +219,30 @@
@UiEvent(doc = "Notification became visible.")
NOTIFICATION_OPEN(197),
@UiEvent(doc = "Notification stopped being visible.")
- NOTIFICATION_CLOSE(198);
+ NOTIFICATION_CLOSE(198),
+ @UiEvent(doc = "Notification was snoozed.")
+ NOTIFICATION_SNOOZED(317),
+ @UiEvent(doc = "Notification was not posted because its app is snoozed.")
+ NOTIFICATION_NOT_POSTED_SNOOZED(319),
+ @UiEvent(doc = "Notification was clicked.")
+ NOTIFICATION_CLICKED(320),
+ @UiEvent(doc = "Notification action was clicked.")
+ NOTIFICATION_ACTION_CLICKED(321),
+ @UiEvent(doc = "Notification detail was expanded due to non-user action.")
+ NOTIFICATION_DETAIL_OPEN_SYSTEM(327),
+ @UiEvent(doc = "Notification detail was collapsed due to non-user action.")
+ NOTIFICATION_DETAIL_CLOSE_SYSTEM(328),
+ @UiEvent(doc = "Notification detail was expanded due to user action.")
+ NOTIFICATION_DETAIL_OPEN_USER(329),
+ @UiEvent(doc = "Notification detail was collapsed due to user action.")
+ NOTIFICATION_DETAIL_CLOSE_USER(330),
+ @UiEvent(doc = "Notification direct reply action was used.")
+ NOTIFICATION_DIRECT_REPLIED(331),
+ @UiEvent(doc = "Notification smart reply action was used.")
+ NOTIFICATION_SMART_REPLIED(332),
+ @UiEvent(doc = "Notification smart reply action was visible.")
+ NOTIFICATION_SMART_REPLY_VISIBLE(333),
+ ;
private final int mId;
NotificationEvent(int id) {
@@ -217,7 +255,29 @@
public static NotificationEvent fromVisibility(boolean visible) {
return visible ? NOTIFICATION_OPEN : NOTIFICATION_CLOSE;
}
+ public static NotificationEvent fromExpanded(boolean expanded, boolean userAction) {
+ if (userAction) {
+ return expanded ? NOTIFICATION_DETAIL_OPEN_USER : NOTIFICATION_DETAIL_CLOSE_USER;
+ }
+ return expanded ? NOTIFICATION_DETAIL_OPEN_SYSTEM : NOTIFICATION_DETAIL_CLOSE_SYSTEM;
+ }
}
+
+ enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Notification panel became visible.")
+ NOTIFICATION_PANEL_OPEN(325),
+ @UiEvent(doc = "Notification panel stopped being visible.")
+ NOTIFICATION_PANEL_CLOSE(326);
+
+ private final int mId;
+ NotificationPanelEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+ }
+
/**
* A helper for extracting logging information from one or two NotificationRecords.
*/
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 9fcac25..494ff31 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -65,20 +65,16 @@
}
@Override
- public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
- log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r);
- }
-
- @Override
- public void logNotificationVisibility(NotificationRecord r, boolean visible) {
- log(NotificationEvent.fromVisibility(visible), r);
- }
-
- void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
+ public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
if (r == null) {
return;
}
mUiEventLogger.logWithInstanceId(event, r.getUid(), r.getSbn().getPackageName(),
r.getSbn().getInstanceId());
}
+
+ @Override
+ public void log(UiEventLogger.UiEventEnum event) {
+ mUiEventLogger.log(event);
+ }
}
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
new file mode 100644
index 0000000..7bbb3b1
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -0,0 +1,196 @@
+/*
+ * 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.notification;
+
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+
+import android.annotation.NonNull;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Helper for querying shortcuts.
+ */
+class ShortcutHelper {
+
+ /**
+ * Listener to call when a shortcut we're tracking has been removed.
+ */
+ interface ShortcutListener {
+ void onShortcutRemoved(String key);
+ }
+
+ private LauncherApps mLauncherAppsService;
+ private ShortcutListener mShortcutListener;
+
+ // Key: packageName Value: <shortcutId, notifId>
+ private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
+ private boolean mLauncherAppsCallbackRegistered;
+
+ // Bubbles can be created based on a shortcut, we need to listen for changes to
+ // that shortcut so that we may update the bubble appropriately.
+ private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() {
+ @Override
+ public void onPackageRemoved(String packageName, UserHandle user) {
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, UserHandle user) {
+ }
+
+ @Override
+ public void onPackageChanged(String packageName, UserHandle user) {
+ }
+
+ @Override
+ public void onPackagesAvailable(String[] packageNames, UserHandle user,
+ boolean replacing) {
+ }
+
+ @Override
+ public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+ boolean replacing) {
+ }
+
+ @Override
+ public void onShortcutsChanged(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+ HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
+ ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
+ if (shortcutBubbles != null) {
+ // If we can't find one of our bubbles in the shortcut list, that bubble needs
+ // to be removed.
+ for (String shortcutId : shortcutBubbles.keySet()) {
+ boolean foundShortcut = false;
+ for (int i = 0; i < shortcuts.size(); i++) {
+ if (shortcuts.get(i).getId().equals(shortcutId)) {
+ foundShortcut = true;
+ break;
+ }
+ }
+ if (!foundShortcut) {
+ bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId));
+ }
+ }
+ }
+
+ // Let NoMan know about the updates
+ for (int i = 0; i < bubbleKeysToRemove.size(); i++) {
+ // update flag bubble
+ String bubbleKey = bubbleKeysToRemove.get(i);
+ if (mShortcutListener != null) {
+ mShortcutListener.onShortcutRemoved(bubbleKey);
+ }
+ }
+ }
+ };
+
+ ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener) {
+ mLauncherAppsService = launcherApps;
+ mShortcutListener = listener;
+ }
+
+ @VisibleForTesting
+ void setLauncherApps(LauncherApps launcherApps) {
+ mLauncherAppsService = launcherApps;
+ }
+
+ ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+ if (mLauncherAppsService == null) {
+ return null;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (shortcutId == null || packageName == null || user == null) {
+ return null;
+ }
+ LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+ query.setPackage(packageName);
+ query.setShortcutIds(Arrays.asList(shortcutId));
+ query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
+ List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
+ return shortcuts != null && shortcuts.size() > 0
+ ? shortcuts.get(0)
+ : null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ boolean hasValidShortcutInfo(String shortcutId, String packageName,
+ UserHandle user) {
+ ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
+ return shortcutInfo != null && shortcutInfo.isLongLived();
+ }
+
+ /**
+ * Shortcut based bubbles require some extra work to listen for shortcut changes.
+ *
+ * @param r the notification record to check
+ * @param removedNotification true if this notification is being removed
+ * @param handler handler to register the callback with
+ */
+ void maybeListenForShortcutChangesForBubbles(NotificationRecord r, boolean removedNotification,
+ Handler handler) {
+ final String shortcutId = r.getNotification().getBubbleMetadata() != null
+ ? r.getNotification().getBubbleMetadata().getShortcutId()
+ : null;
+ if (shortcutId == null) {
+ return;
+ }
+ if (r.getNotification().isBubbleNotification() && !removedNotification) {
+ // Must track shortcut based bubbles in case the shortcut is removed
+ HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
+ r.getSbn().getPackageName());
+ if (packageBubbles == null) {
+ packageBubbles = new HashMap<>();
+ }
+ packageBubbles.put(shortcutId, r.getKey());
+ mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
+ if (!mLauncherAppsCallbackRegistered) {
+ mLauncherAppsService.registerCallback(mLauncherAppsCallback, handler);
+ mLauncherAppsCallbackRegistered = true;
+ }
+ } else {
+ // No longer track shortcut
+ HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
+ r.getSbn().getPackageName());
+ if (packageBubbles != null) {
+ packageBubbles.remove(shortcutId);
+ }
+ if (packageBubbles != null && packageBubbles.isEmpty()) {
+ mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
+ }
+ if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
+ mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
+ mLauncherAppsCallbackRegistered = false;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f45e66e..2853956 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -141,6 +141,7 @@
updateDefaultAutomaticRuleNames();
mConfig = mDefaultConfig.copy();
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
+ mConsolidatedPolicy = mConfig.toNotificationPolicy();
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
@@ -821,9 +822,6 @@
* @return a copy of the zen mode consolidated policy
*/
public Policy getConsolidatedNotificationPolicy() {
- if (mConsolidatedPolicy == null) {
- return null;
- }
return mConsolidatedPolicy.copy();
}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 6c2d77c..8349632 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -66,7 +66,7 @@
@RequiresPermission(android.Manifest.permission.DUMP)
public void startBugreport(int callingUidUnused, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener) {
+ int bugreportMode, IDumpstateListener listener, boolean isScreenshotRequested) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
Objects.requireNonNull(callingPackage);
Objects.requireNonNull(bugreportFd);
@@ -88,7 +88,7 @@
}
synchronized (mLock) {
startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd,
- bugreportMode, listener);
+ bugreportMode, listener, isScreenshotRequested);
}
}
@@ -145,7 +145,7 @@
@GuardedBy("mLock")
private void startBugreportLocked(int callingUid, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener) {
+ int bugreportMode, IDumpstateListener listener, boolean isScreenshotRequested) {
if (isDumpstateBinderServiceRunningLocked()) {
Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport"
+ " while another one is currently in progress.");
@@ -165,7 +165,7 @@
IDumpstateListener myListener = new DumpstateListener(listener, ds);
try {
ds.startBugreport(callingUid, callingPackage,
- bugreportFd, screenshotFd, bugreportMode, myListener);
+ bugreportFd, screenshotFd, bugreportMode, myListener, isScreenshotRequested);
} catch (RemoteException e) {
// bugreportd service is already started now. We need to kill it to manage the
// lifecycle correctly. If we don't subsequent callers will get
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index d12e03d..c37ea8b 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -301,6 +301,14 @@
public abstract boolean destroyDeSnapshots(int rollbackId);
/**
+ * Deletes snapshots of the credential encrypted apex data directories for the specified user,
+ * where the rollback id is not included in {@code retainRollbackIds}.
+ *
+ * @return boolean true if the delete was successful
+ */
+ public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds);
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -745,6 +753,17 @@
}
}
+ @Override
+ public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
+ try {
+ mApexService.destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
+ return true;
+ } catch (Exception e) {
+ Slog.e(TAG, e.getMessage(), e);
+ return false;
+ }
+ }
+
/**
* Dump information about the packages contained in a particular cache
* @param packagesCache the cache to print information about.
@@ -963,6 +982,11 @@
}
@Override
+ public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
+ return true;
+ }
+
+ @Override
void dump(PrintWriter pw, String packageName) {
// No-op
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 79a4da2..690b9f7 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -481,6 +481,12 @@
mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
}
}
+ // if either package instruments the other, mark both as visible to one another
+ if (pkgInstruments(newPkgSetting, existingSetting)
+ || pkgInstruments(existingSetting, newPkgSetting)) {
+ mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
+ mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+ }
}
int existingSize = existingSettings.size();
@@ -715,19 +721,6 @@
Trace.endSection();
}
- if (callingPkgSetting != null) {
- if (callingPkgInstruments(callingPkgSetting, targetPkgSetting, targetName)) {
- return false;
- }
- } else {
- for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
- if (callingPkgInstruments(callingSharedPkgSettings.valueAt(i),
- targetPkgSetting, targetName)) {
- return false;
- }
- }
- }
-
try {
Trace.beginSection("mOverlayReferenceMapper");
if (callingSharedPkgSettings != null) {
@@ -762,16 +755,16 @@
}
}
- private static boolean callingPkgInstruments(PackageSetting callingPkgSetting,
- PackageSetting targetPkgSetting,
- String targetName) {
+ /** Returns {@code true} if the source package instruments the target package. */
+ private static boolean pkgInstruments(PackageSetting source, PackageSetting target) {
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingPkgInstruments");
- final List<ParsedInstrumentation> inst = callingPkgSetting.pkg.getInstrumentations();
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "pkgInstruments");
+ final String packageName = target.pkg.getPackageName();
+ final List<ParsedInstrumentation> inst = source.pkg.getInstrumentations();
for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
- if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) {
+ if (Objects.equals(inst.get(i).getTargetPackage(), packageName)) {
if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "instrumentation");
+ log(source, target, "instrumentation");
}
return true;
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index ec9b37d..83da381 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -45,6 +45,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -183,7 +184,8 @@
String callingFeatureId,
Intent intent,
@UserIdInt int userId,
- IBinder callingActivity) throws RemoteException {
+ IBinder callingActivity,
+ Bundle options) throws RemoteException {
Objects.requireNonNull(callingPackage);
Objects.requireNonNull(intent);
Objects.requireNonNull(intent.getComponent(), "The intent must have a Component set");
@@ -226,7 +228,7 @@
launchIntent,
callingActivity,
/* startFlags= */ 0,
- /* options= */ null,
+ options,
userId);
logStartActivityByIntent(callingPackage);
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 8031eaa..1b271a7 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -819,6 +819,18 @@
}
@Override
+ public String getShortcutIconUri(String callingPackage, String packageName,
+ String shortcutId, int userId) {
+ ensureShortcutPermission(callingPackage);
+ if (!canAccessProfile(userId, "Cannot access shortcuts")) {
+ return null;
+ }
+
+ return mShortcutServiceInternal.getShortcutIconUri(getCallingUserId(), callingPackage,
+ packageName, shortcutId, userId);
+ }
+
+ @Override
public boolean hasShortcutHostPermission(String callingPackage) {
verifyCallingPackage(callingPackage);
return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 33ef2d4..2ff3d2a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -47,7 +47,6 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
-import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -805,26 +804,30 @@
public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
- return session != null ? session.generateInfo() : null;
+
+ return session != null
+ ? session.generateInfoForCaller(true /*withIcon*/, Binder.getCallingUid())
+ : null;
}
}
@Override
public ParceledListSlice<SessionInfo> getStagedSessions() {
- return mStagingManager.getSessions();
+ return mStagingManager.getSessions(Binder.getCallingUid());
}
@Override
public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
+ final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(
- Binder.getCallingUid(), userId, true, false, "getAllSessions");
+ callingUid, userId, true, false, "getAllSessions");
final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
if (session.userId == userId && !session.hasParentSessionId()) {
- result.add(session.generateInfo(false));
+ result.add(session.generateInfoForCaller(false, callingUid));
}
}
}
@@ -842,7 +845,8 @@
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
- SessionInfo info = session.generateInfo(false);
+ SessionInfo info =
+ session.generateInfoForCaller(false /*withIcon*/, Process.SYSTEM_UID);
if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
&& session.userId == userId && !session.hasParentSessionId()) {
result.add(info);
@@ -1033,80 +1037,11 @@
}
}
- static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
- Throwable cause) {
- final Intent intent = new Intent();
- intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING);
- if (cause != null && !TextUtils.isEmpty(cause.getMessage())) {
- intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
- "Staging Image Not Ready [" + cause.getMessage() + "]");
- } else {
- intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
- }
- try {
- target.sendIntent(context, 0, intent, null, null);
- } catch (SendIntentException ignored) {
- }
- }
-
- static void sendOnUserActionRequired(Context context, IntentSender target, int sessionId,
- Intent intent) {
- final Intent fillIn = new Intent();
- fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
- fillIn.putExtra(Intent.EXTRA_INTENT, intent);
- try {
- target.sendIntent(context, 0, fillIn, null, null);
- } catch (SendIntentException ignored) {
- }
- }
-
- static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
- boolean showNotification, int userId, String basePackageName, int returnCode,
- String msg, Bundle extras) {
- if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) {
- boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
- Notification notification = buildSuccessNotification(context,
- context.getResources()
- .getString(update ? R.string.package_updated_device_owner :
- R.string.package_installed_device_owner),
- basePackageName,
- userId);
- if (notification != null) {
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(basePackageName,
- SystemMessage.NOTE_PACKAGE_STATE,
- notification);
- }
- }
- final Intent fillIn = new Intent();
- fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
- fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
- PackageManager.installStatusToPublicStatus(returnCode));
- fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
- PackageManager.installStatusToString(returnCode, msg));
- fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
- if (extras != null) {
- final String existing = extras.getString(
- PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
- if (!TextUtils.isEmpty(existing)) {
- fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
- }
- }
- try {
- target.sendIntent(context, 0, fillIn, null, null);
- } catch (SendIntentException ignored) {
- }
- }
-
/**
* Build a notification for package installation / deletion by device owners that is shown if
* the operation succeeds.
*/
- private static Notification buildSuccessNotification(Context context, String contentText,
+ static Notification buildSuccessNotification(Context context, String contentText,
String basePackageName, int userId) {
PackageInfo packageInfo = null;
try {
@@ -1302,7 +1237,10 @@
session.markUpdated();
writeSessionsAsync();
if (mOkToSendBroadcasts) {
- mPm.sendSessionUpdatedBroadcast(session.generateInfo(false),
+ // we don't scrub the data here as this is sent only to the installer several
+ // privileged system packages
+ mPm.sendSessionUpdatedBroadcast(
+ session.generateInfoForCaller(false/*icon*/, Process.SYSTEM_UID),
session.userId);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 97aa79d..45a25a0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.content.pm.DataLoaderType.INCREMENTAL;
import static android.content.pm.DataLoaderType.STREAMING;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
@@ -50,6 +51,8 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
@@ -118,9 +121,11 @@
import android.util.SparseIntArray;
import android.util.apk.ApkSignatureVerifier;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
+import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -166,6 +171,8 @@
private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
"whitelisted-restricted-permission";
+ private static final String TAG_AUTO_REVOKE_PERMISSIONS_MODE =
+ "auto-revoke-permissions-mode";
private static final String ATTR_SESSION_ID = "sessionId";
private static final String ATTR_USER_ID = "userId";
private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
@@ -311,8 +318,41 @@
@GuardedBy("mLock")
private int mParentSessionId;
+ static class FileEntry {
+ private final int mIndex;
+ private final InstallationFile mFile;
+
+ FileEntry(int index, InstallationFile file) {
+ this.mIndex = index;
+ this.mFile = file;
+ }
+
+ int getIndex() {
+ return this.mIndex;
+ }
+
+ InstallationFile getFile() {
+ return this.mFile;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof FileEntry)) {
+ return false;
+ }
+ final FileEntry rhs = (FileEntry) obj;
+ return (mFile.getLocation() == rhs.mFile.getLocation()) && TextUtils.equals(
+ mFile.getName(), rhs.mFile.getName());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFile.getLocation(), mFile.getName());
+ }
+ }
+
@GuardedBy("mLock")
- private ArrayList<InstallationFile> mFiles = new ArrayList<>();
+ private ArraySet<FileEntry> mFiles = new ArraySet<>();
@GuardedBy("mLock")
private boolean mStagedSessionApplied;
@@ -404,8 +444,7 @@
final int returnCode = args.argi1;
args.recycle();
- PackageInstallerService.sendOnPackageInstalled(mContext,
- statusReceiver, sessionId,
+ sendOnPackageInstalled(mContext, statusReceiver, sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId,
packageName, returnCode, message, extras);
@@ -516,8 +555,12 @@
this.mParentSessionId = parentSessionId;
if (files != null) {
- for (InstallationFile file : files) {
- mFiles.add(file);
+ for (int i = 0, size = files.length; i < size; ++i) {
+ InstallationFile file = files[i];
+ if (!mFiles.add(new FileEntry(i, file))) {
+ throw new IllegalArgumentException(
+ "Trying to add a duplicate installation file");
+ }
}
}
@@ -557,11 +600,41 @@
}
}
- public SessionInfo generateInfo() {
- return generateInfo(true);
+ /**
+ * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially
+ * sensitive data scrubbed from its fields.
+ *
+ * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
+ * need to be scrubbed
+ */
+ private boolean shouldScrubData(int callingUid) {
+ return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid);
}
- public SessionInfo generateInfo(boolean includeIcon) {
+ /**
+ * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields
+ * that may contain sensitive info being filtered.
+ *
+ * @param includeIcon true if the icon should be included in the object
+ * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
+ * need to be scrubbed
+ * @see #shouldScrubData(int)
+ */
+ public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) {
+ return generateInfoInternal(includeIcon, shouldScrubData(callingUid));
+ }
+
+ /**
+ * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields.
+ *
+ * @param includeIcon true if the icon should be included in the object
+ * @see #generateInfoForCaller(boolean, int)
+ */
+ public SessionInfo generateInfoScrubbed(boolean includeIcon) {
+ return generateInfoInternal(includeIcon, true /*scrubData*/);
+ }
+
+ private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) {
final SessionInfo info = new SessionInfo();
synchronized (mLock) {
info.sessionId = sessionId;
@@ -584,11 +657,16 @@
info.appLabel = params.appLabel;
info.installLocation = params.installLocation;
- info.originatingUri = params.originatingUri;
+ if (!scrubData) {
+ info.originatingUri = params.originatingUri;
+ }
info.originatingUid = params.originatingUid;
- info.referrerUri = params.referrerUri;
+ if (!scrubData) {
+ info.referrerUri = params.referrerUri;
+ }
info.grantedRuntimePermissions = params.grantedRuntimePermissions;
info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions;
+ info.autoRevokePermissionsMode = params.autoRevokePermissionsMode;
info.installFlags = params.installFlags;
info.isMultiPackage = params.isMultiPackage;
info.isStaged = params.isStaged;
@@ -716,9 +794,19 @@
return result;
}
- String[] result = new String[mFiles.size()];
- for (int i = 0, size = mFiles.size(); i < size; ++i) {
- result[i] = mFiles.get(i).getName();
+ InstallationFile[] files = getInstallationFilesLocked();
+ String[] result = new String[files.length];
+ for (int i = 0, size = files.length; i < size; ++i) {
+ result[i] = files[i].getName();
+ }
+ return result;
+ }
+
+ @GuardedBy("mLock")
+ private InstallationFile[] getInstallationFilesLocked() {
+ final InstallationFile[] result = new InstallationFile[mFiles.size()];
+ for (FileEntry fileEntry : mFiles) {
+ result[fileEntry.getIndex()] = fileEntry.getFile();
}
return result;
}
@@ -1551,8 +1639,7 @@
}
}
if (!success) {
- PackageInstallerService.sendOnPackageInstalled(mContext,
- mRemoteStatusReceiver, sessionId,
+ sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
@@ -1599,8 +1686,7 @@
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- PackageInstallerService.sendOnUserActionRequired(mContext,
- mRemoteStatusReceiver, sessionId, intent);
+ sendOnUserActionRequired(mContext, mRemoteStatusReceiver, sessionId, intent);
// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
@@ -2410,7 +2496,10 @@
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("addFile");
- mFiles.add(new InstallationFile(location, name, lengthBytes, metadata, signature));
+ if (!mFiles.add(new FileEntry(mFiles.size(),
+ new InstallationFile(location, name, lengthBytes, metadata, signature)))) {
+ throw new IllegalArgumentException("File already added: " + name);
+ }
}
}
@@ -2428,7 +2517,10 @@
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("removeFile");
- mFiles.add(new InstallationFile(location, getRemoveMarkerName(name), -1, null, null));
+ if (!mFiles.add(new FileEntry(mFiles.size(),
+ new InstallationFile(location, getRemoveMarkerName(name), -1, null, null)))) {
+ throw new IllegalArgumentException("File already removed: " + name);
+ }
}
}
@@ -2448,7 +2540,8 @@
final List<InstallationFileParcel> addedFiles = new ArrayList<>();
final List<String> removedFiles = new ArrayList<>();
- for (InstallationFile file : mFiles) {
+ final InstallationFile[] files = getInstallationFilesLocked();
+ for (InstallationFile file : files) {
if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) {
addedFiles.add(file.getData());
continue;
@@ -2528,9 +2621,8 @@
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
- PackageInstallerService.sendPendingStreaming(mContext,
- mRemoteStatusReceiver,
- sessionId, new StreamingException(e));
+ sendPendingStreaming(mContext, mRemoteStatusReceiver, sessionId,
+ new StreamingException(e));
}
}
};
@@ -2664,7 +2756,7 @@
final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()
&& (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
- mPm.sendSessionCommitBroadcast(generateInfo(), userId);
+ mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId);
}
mCallback.onSessionFinished(this, success);
@@ -2832,6 +2924,75 @@
pw.decreaseIndent();
}
+ private static void sendOnUserActionRequired(Context context, IntentSender target,
+ int sessionId, Intent intent) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
+ fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+ try {
+ target.sendIntent(context, 0, fillIn, null, null);
+ } catch (IntentSender.SendIntentException ignored) {
+ }
+ }
+
+ private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
+ boolean showNotification, int userId, String basePackageName, int returnCode,
+ String msg, Bundle extras) {
+ if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) {
+ boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
+ Notification notification = PackageInstallerService.buildSuccessNotification(context,
+ context.getResources()
+ .getString(update ? R.string.package_updated_device_owner :
+ R.string.package_installed_device_owner),
+ basePackageName,
+ userId);
+ if (notification != null) {
+ NotificationManager notificationManager = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.notify(basePackageName,
+ SystemMessageProto.SystemMessage.NOTE_PACKAGE_STATE,
+ notification);
+ }
+ }
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
+ fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageManager.installStatusToPublicStatus(returnCode));
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+ PackageManager.installStatusToString(returnCode, msg));
+ fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+ if (extras != null) {
+ final String existing = extras.getString(
+ PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
+ if (!TextUtils.isEmpty(existing)) {
+ fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
+ }
+ }
+ try {
+ target.sendIntent(context, 0, fillIn, null, null);
+ } catch (IntentSender.SendIntentException ignored) {
+ }
+ }
+
+ private static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
+ Throwable cause) {
+ final Intent intent = new Intent();
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+ intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING);
+ if (cause != null && !TextUtils.isEmpty(cause.getMessage())) {
+ intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+ "Staging Image Not Ready [" + cause.getMessage() + "]");
+ } else {
+ intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
+ }
+ try {
+ target.sendIntent(context, 0, intent, null, null);
+ } catch (IntentSender.SendIntentException ignored) {
+ }
+ }
+
private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
String[] grantedRuntimePermissions) throws IOException {
if (grantedRuntimePermissions != null) {
@@ -2855,6 +3016,13 @@
}
}
+ private static void writeAutoRevokePermissionsMode(@NonNull XmlSerializer out, int mode)
+ throws IOException {
+ out.startTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
+ writeIntAttribute(out, ATTR_MODE, mode);
+ out.endTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
+ }
+
private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
return new File(sessionsDir, "app_icon." + sessionId + ".png");
@@ -2935,6 +3103,7 @@
writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
writeWhitelistedRestrictedPermissionsLocked(out,
params.whitelistedRestrictedPermissions);
+ writeAutoRevokePermissionsMode(out, params.autoRevokePermissionsMode);
// Persist app icon if changed since last written
File appIconFile = buildAppIconFile(sessionId, sessionsDir);
@@ -2961,7 +3130,9 @@
writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
out.endTag(null, TAG_CHILD_SESSION);
}
- for (InstallationFile file : mFiles) {
+
+ final InstallationFile[] files = getInstallationFilesLocked();
+ for (InstallationFile file : getInstallationFilesLocked()) {
out.startTag(null, TAG_SESSION_FILE);
writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
writeStringAttribute(out, ATTR_NAME, file.getName());
@@ -3078,6 +3249,7 @@
// depth.
List<String> grantedRuntimePermissions = new ArrayList<>();
List<String> whitelistedRestrictedPermissions = new ArrayList<>();
+ int autoRevokePermissionsMode = MODE_DEFAULT;
List<Integer> childSessionIds = new ArrayList<>();
List<InstallationFile> files = new ArrayList<>();
int outerDepth = in.getDepth();
@@ -3094,6 +3266,9 @@
whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME));
}
+ if (TAG_AUTO_REVOKE_PERMISSIONS_MODE.equals(in.getName())) {
+ autoRevokePermissionsMode = readIntAttribute(in, ATTR_MODE);
+ }
if (TAG_CHILD_SESSION.equals(in.getName())) {
childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
}
@@ -3116,6 +3291,8 @@
params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
}
+ params.autoRevokePermissionsMode = autoRevokePermissionsMode;
+
int[] childSessionIdsArray;
if (childSessionIds.size() > 0) {
childSessionIdsArray = new int[childSessionIds.size()];
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b1e32cc..09e4e60 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,6 +24,9 @@
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
@@ -1653,12 +1656,13 @@
&& parentRes.pkg != null)
? parentRes.pkg.getRequestedPermissions()
: args.whitelistedRestrictedPermissions;
+ int autoRevokePermissionsMode = args.autoRevokePermissionsMode;
// Handle the parent package
handlePackagePostInstall(parentRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
- whitelistedRestrictedPermissions, didRestore,
- args.installSource.installerPackageName, args.observer,
+ whitelistedRestrictedPermissions, autoRevokePermissionsMode,
+ didRestore, args.installSource.installerPackageName, args.observer,
args.mDataLoaderType);
// Handle the child packages
@@ -1668,7 +1672,8 @@
PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
handlePackagePostInstall(childRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
- whitelistedRestrictedPermissions, false /*didRestore*/,
+ whitelistedRestrictedPermissions, autoRevokePermissionsMode,
+ false /*didRestore*/,
args.installSource.installerPackageName, args.observer,
args.mDataLoaderType);
}
@@ -1997,6 +2002,7 @@
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
boolean killApp, boolean virtualPreload,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
+ int autoRevokePermissionsMode,
boolean launchedForRestore, String installerPackage,
IPackageInstallObserver2 installObserver, int dataLoaderType) {
final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
@@ -2017,6 +2023,11 @@
Process.myUid(), FLAG_PERMISSION_WHITELIST_INSTALLER);
}
+ if (autoRevokePermissionsMode == MODE_ALLOWED || autoRevokePermissionsMode == MODE_IGNORED) {
+ mPermissionManager.setAutoRevokeWhitelisted(res.pkg.getPackageName(),
+ autoRevokePermissionsMode == MODE_IGNORED, UserHandle.myUserId());
+ }
+
// Now that we successfully installed the package, grant runtime
// permissions if requested before broadcasting the install. Also
// for legacy apps in permission review mode we clear the permission
@@ -11512,8 +11523,8 @@
"Static shared libs cannot declare permission groups");
}
- // Static shared libs cannot declare features
- if (!pkg.getFeatures().isEmpty()) {
+ // Static shared libs cannot declare attributions
+ if (!pkg.getAttributions().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare features");
}
@@ -14293,6 +14304,7 @@
final String packageAbiOverride;
final String[] grantedRuntimePermissions;
final List<String> whitelistedRestrictedPermissions;
+ final int autoRevokePermissionsMode;
final VerificationInfo verificationInfo;
final PackageParser.SigningDetails signingDetails;
final int installReason;
@@ -14307,6 +14319,7 @@
int installFlags, InstallSource installSource, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
+ int autoRevokePermissionsMode,
SigningDetails signingDetails, int installReason,
long requiredInstalledVersionCode, int dataLoaderType) {
super(user);
@@ -14320,6 +14333,7 @@
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
+ this.autoRevokePermissionsMode = autoRevokePermissionsMode;
this.signingDetails = signingDetails;
this.installReason = installReason;
this.requiredInstalledVersionCode = requiredInstalledVersionCode;
@@ -14356,6 +14370,7 @@
packageAbiOverride = sessionParams.abiOverride;
grantedRuntimePermissions = sessionParams.grantedRuntimePermissions;
whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
+ autoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode;
signingDetails = activeInstallSession.getSigningDetails();
requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
forceQueryableOverride = sessionParams.forceQueryableOverride;
@@ -14724,9 +14739,8 @@
verificationState.setRequiredVerifierUid(requiredUid);
final int installerUid =
verificationInfo == null ? -1 : verificationInfo.installerUid;
- if (!origin.existing && requiredUid != -1
- && isVerificationEnabled(
- pkgLite, verifierUser.getIdentifier(), installFlags, installerUid)) {
+ if (!origin.existing && isVerificationEnabled(pkgLite, verifierUser.getIdentifier(),
+ installFlags, installerUid)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -14792,9 +14806,9 @@
}
}
- final ComponentName requiredVerifierComponent = matchComponentForVerifier(
- mRequiredVerifierPackage, receivers);
if (mRequiredVerifierPackage != null) {
+ final ComponentName requiredVerifierComponent = matchComponentForVerifier(
+ mRequiredVerifierPackage, receivers);
/*
* Send the intent to the required verification agent,
* but only start the verification timeout after the
@@ -14952,6 +14966,7 @@
final String abiOverride;
final String[] installGrantPermissions;
final List<String> whitelistedRestrictedPermissions;
+ final int autoRevokePermissionsMode;
/** If non-null, drop an async trace when the install completes */
final String traceMethod;
final int traceCookie;
@@ -14971,6 +14986,7 @@
UserHandle user, String[] instructionSets,
String abiOverride, String[] installGrantPermissions,
List<String> whitelistedRestrictedPermissions,
+ int autoRevokePermissionsMode,
String traceMethod, int traceCookie, SigningDetails signingDetails,
int installReason, boolean forceQueryableOverride,
MultiPackageInstallParams multiPackageInstallParams, int dataLoaderType) {
@@ -14985,6 +15001,7 @@
this.abiOverride = abiOverride;
this.installGrantPermissions = installGrantPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
+ this.autoRevokePermissionsMode = autoRevokePermissionsMode;
this.traceMethod = traceMethod;
this.traceCookie = traceCookie;
this.signingDetails = signingDetails;
@@ -15000,6 +15017,7 @@
params.installSource, params.volumeUuid,
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
+ params.autoRevokePermissionsMode,
params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason, params.forceQueryableOverride,
params.mParentInstallParams, params.mDataLoaderType);
@@ -15091,7 +15109,7 @@
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
- null, null, instructionSets, null, null, null, null, 0,
+ null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */,
DataLoaderType.NONE);
@@ -22457,7 +22475,8 @@
final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
installSource, volumeUuid, null /*verificationInfo*/, user,
packageAbiOverride, null /*grantedPermissions*/,
- null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
+ null /*whitelistedRestrictedPermissions*/, MODE_DEFAULT /* autoRevokePermissions */,
+ PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST,
DataLoaderType.NONE);
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
@@ -24159,6 +24178,12 @@
}
mAppsFilter.getFeatureConfig().enableLogging(pkg.appId, enable);
}
+
+ @Override
+ public boolean isSystemPackage(@NonNull String packageName) {
+ return packageName.equals(
+ PackageManagerService.this.ensureSystemPackageName(packageName));
+ }
}
@GuardedBy("mLock")
@@ -24347,6 +24372,24 @@
}
@Override
+ public boolean isAutoRevokeWhitelisted(String packageName) {
+ int mode = mInjector.getAppOpsManager().checkOpNoThrow(
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ Binder.getCallingUid(), packageName);
+ if (mode == MODE_ALLOWED) {
+ return false;
+ } else if (mode == MODE_IGNORED) {
+ return true;
+ } else {
+ synchronized (mLock) {
+ boolean manifestWhitelisted =
+ mPackages.get(packageName).isAllowDontAutoRevokePermmissions();
+ return manifestWhitelisted;
+ }
+ }
+ }
+
+ @Override
public int getInstallReason(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index be17dd8..8a9f1b3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3017,9 +3017,11 @@
private int doAddFiles(int sessionId, ArrayList<String> args, long sessionSizeBytes,
boolean isApex) throws RemoteException {
- PackageInstaller.Session session = new PackageInstaller.Session(
- mInterface.getPackageInstaller().openSession(sessionId));
+ PackageInstaller.Session session = null;
try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
// 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
final String name = "base." + (isApex ? "apex" : "apk");
@@ -3043,6 +3045,10 @@
}
}
return 0;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Failed to add file(s), reason: " + e);
+ getOutPrintWriter().println("Failure [failed to add file(s)]");
+ return 1;
} finally {
IoUtils.closeQuietly(session);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
index dc534a7..1c5f0a7 100644
--- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
@@ -28,7 +28,6 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
import libcore.io.IoUtils;
@@ -150,9 +149,10 @@
shortcut.setIconResourceId(0);
shortcut.setIconResName(null);
shortcut.setBitmapPath(null);
+ shortcut.setIconUri(null);
shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE |
ShortcutInfo.FLAG_ADAPTIVE_BITMAP | ShortcutInfo.FLAG_HAS_ICON_RES |
- ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE);
+ ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE | ShortcutInfo.FLAG_HAS_ICON_URI);
}
public void saveBitmapLocked(ShortcutInfo shortcut,
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index c8df5c7..1fc0a38 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -102,6 +102,7 @@
private static final String ATTR_ICON_RES_ID = "icon-res";
private static final String ATTR_ICON_RES_NAME = "icon-resname";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
+ private static final String ATTR_ICON_URI = "icon-uri";
private static final String ATTR_LOCUS_ID = "locus-id";
private static final String ATTR_PERSON_NAME = "name";
@@ -1628,7 +1629,8 @@
int flags = si.getFlags() &
~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
| ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE
- | ShortcutInfo.FLAG_DYNAMIC);
+ | ShortcutInfo.FLAG_DYNAMIC
+ | ShortcutInfo.FLAG_HAS_ICON_URI | ShortcutInfo.FLAG_ADAPTIVE_BITMAP);
ShortcutService.writeAttr(out, ATTR_FLAGS, flags);
// Set the publisher version code at every backup.
@@ -1646,6 +1648,7 @@
ShortcutService.writeAttr(out, ATTR_ICON_RES_ID, si.getIconResourceId());
ShortcutService.writeAttr(out, ATTR_ICON_RES_NAME, si.getIconResName());
ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
+ ShortcutService.writeAttr(out, ATTR_ICON_URI, si.getIconUri());
}
if (shouldBackupDetails) {
@@ -1764,6 +1767,7 @@
int iconResId;
String iconResName;
String bitmapPath;
+ String iconUri;
final String locusIdString;
int backupVersionCode;
ArraySet<String> categories = null;
@@ -1791,6 +1795,7 @@
iconResId = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES_ID);
iconResName = ShortcutService.parseStringAttribute(parser, ATTR_ICON_RES_NAME);
bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
+ iconUri = ShortcutService.parseStringAttribute(parser, ATTR_ICON_URI);
locusIdString = ShortcutService.parseStringAttribute(parser, ATTR_LOCUS_ID);
final int outerDepth = parser.getDepth();
@@ -1866,8 +1871,8 @@
categories,
intents.toArray(new Intent[intents.size()]),
rank, extras, lastChangedTimestamp, flags,
- iconResId, iconResName, bitmapPath, disabledReason,
- persons.toArray(new Person[persons.size()]), locusId);
+ iconResId, iconResName, bitmapPath, iconUri,
+ disabledReason, persons.toArray(new Person[persons.size()]), locusId);
}
private static Intent parseIntent(XmlPullParser parser)
@@ -1991,16 +1996,26 @@
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ " still has an icon");
}
- if (si.hasAdaptiveBitmap() && !si.hasIconFile()) {
+ if (si.hasAdaptiveBitmap() && !(si.hasIconFile() || si.hasIconUri())) {
failed = true;
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
- + " has adaptive bitmap but was not saved to a file.");
+ + " has adaptive bitmap but was not saved to a file nor has icon uri.");
}
if (si.hasIconFile() && si.hasIconResource()) {
failed = true;
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ " has both resource and bitmap icons");
}
+ if (si.hasIconFile() && si.hasIconUri()) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " has both url and bitmap icons");
+ }
+ if (si.hasIconUri() && si.hasIconResource()) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " has both url and resource icons");
+ }
if (si.isEnabled()
!= (si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED)) {
failed = true;
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index f9c0db0..d3aace1 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -449,6 +449,7 @@
iconResId,
null, // icon res name
null, // bitmap path
+ null, // icon Url
disabledReason,
null /* persons */,
null /* locusId */);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 66f3574..8768ab0 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -24,6 +24,8 @@
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.IUidObserver;
+import android.app.IUriGrantsManager;
+import android.app.UriGrantsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
@@ -65,6 +67,7 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -106,6 +109,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
+import com.android.server.uri.UriGrantsManagerInternal;
import libcore.io.IoUtils;
@@ -327,6 +331,9 @@
private final UserManagerInternal mUserManagerInternal;
private final UsageStatsManagerInternal mUsageStatsManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
+ private final IUriGrantsManager mUriGrantsManager;
+ private final UriGrantsManagerInternal mUriGrantsManagerInternal;
+ private final IBinder mUriPermissionOwner;
private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor;
private final ShortcutBitmapSaver mShortcutBitmapSaver;
@@ -449,6 +456,11 @@
mActivityManagerInternal = Objects.requireNonNull(
LocalServices.getService(ActivityManagerInternal.class));
+ mUriGrantsManager = UriGrantsManager.getService();
+ mUriGrantsManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(UriGrantsManagerInternal.class));
+ mUriPermissionOwner = mUriGrantsManagerInternal.newUriPermissionOwner(TAG);
+
mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
mShortcutBitmapSaver = new ShortcutBitmapSaver(this);
mShortcutDumpFiles = new ShortcutDumpFiles(this);
@@ -1414,7 +1426,7 @@
}
void saveIconAndFixUpShortcutLocked(ShortcutInfo shortcut) {
- if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
+ if (shortcut.hasIconFile() || shortcut.hasIconResource() || shortcut.hasIconUri()) {
return;
}
@@ -1438,6 +1450,15 @@
shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
return;
}
+ case Icon.TYPE_URI:
+ shortcut.setIconUri(icon.getUriString());
+ shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_URI);
+ return;
+ case Icon.TYPE_URI_ADAPTIVE_BITMAP:
+ shortcut.setIconUri(icon.getUriString());
+ shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_URI
+ | ShortcutInfo.FLAG_ADAPTIVE_BITMAP);
+ return;
case Icon.TYPE_BITMAP:
bitmap = icon.getBitmap(); // Don't recycle in this case.
break;
@@ -3129,6 +3150,59 @@
}
@Override
+ public String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ Objects.requireNonNull(launcherPackage, "launcherPackage");
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(shortcutId, "shortcutId");
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
+ getLauncherShortcutsLocked(launcherPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave();
+
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return null;
+ }
+
+ final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
+ if (shortcutInfo == null || !shortcutInfo.hasIconUri()) {
+ return null;
+ }
+ String uri = shortcutInfo.getIconUri();
+ if (uri == null) {
+ Slog.w(TAG, "null uri detected in getShortcutIconUri()");
+ return null;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int packageUid = mPackageManagerInternal.getPackageUidInternal(packageName,
+ PackageManager.MATCH_DIRECT_BOOT_AUTO, userId);
+ // Grant read uri permission to the caller on behalf of the shortcut owner. All
+ // granted permissions are revoked when the default launcher changes, or when
+ // device is rebooted.
+ // b/151572645 is tracking a bug where Uri permissions are persisted across
+ // reboots, even when Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION is not used.
+ mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid,
+ launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ userId, launcherUserId);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to grant uri access to " + launcherPackage + " for " + uri,
+ e);
+ uri = null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return uri;
+ }
+ }
+
+ @Override
public boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage, int callingPid, int callingUid) {
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId,
@@ -3241,7 +3315,14 @@
user.clearLauncher();
}
if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) {
- // Nothing farther to do.
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ final ComponentName lastLauncher = user.getLastKnownLauncher();
+ final ComponentName currentLauncher = getDefaultLauncher(userId);
+ if (currentLauncher == null || !currentLauncher.equals(lastLauncher)) {
+ // Default launcher is removed or changed, revoke all URI permissions.
+ mUriGrantsManagerInternal.revokeUriPermissionFromOwner(mUriPermissionOwner,
+ null, ~0, 0);
+ }
return;
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 83fe556..342c907 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -128,11 +128,12 @@
}
}
- ParceledListSlice<PackageInstaller.SessionInfo> getSessions() {
+ ParceledListSlice<PackageInstaller.SessionInfo> getSessions(int callingUid) {
final List<PackageInstaller.SessionInfo> result = new ArrayList<>();
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
- result.add(mStagedSessions.valueAt(i).generateInfo(false));
+ final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
+ result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid));
}
}
return new ParceledListSlice<>(result);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index df3c83a..8280a61 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -455,11 +455,13 @@
@Override
public void onFinished(int id, Bundle extras) {
- try {
- mContext.startIntentSender(mTarget, null, 0, 0, 0);
- } catch (IntentSender.SendIntentException e) {
- Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
- }
+ mHandler.post(() -> {
+ try {
+ mContext.startIntentSender(mTarget, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
+ }
+ });
}
}
@@ -1300,14 +1302,15 @@
@Override
public boolean hasBadge(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasBadge");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasBadge");
final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
return userTypeDetails != null && userTypeDetails.hasBadge();
}
@Override
public @StringRes int getUserBadgeLabelResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeLabelResId");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "getUserBadgeLabelResId");
final UserInfo userInfo = getUserInfoNoChecks(userId);
final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1320,7 +1323,8 @@
@Override
public @ColorRes int getUserBadgeColorResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeColorResId");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "getUserBadgeColorResId");
final UserInfo userInfo = getUserInfoNoChecks(userId);
final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1333,7 +1337,7 @@
@Override
public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
Slog.e(LOG_TAG, "Requested icon badge for non-badged user " + userId);
@@ -1344,7 +1348,7 @@
@Override
public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
Slog.e(LOG_TAG, "Requested badge for non-badged user " + userId);
@@ -1355,7 +1359,7 @@
@Override
public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId,
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
"getUserBadgeNoBackgroundResId");
final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1367,7 +1371,7 @@
@Override
public boolean isProfile(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isProfile");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.isProfile();
@@ -1376,7 +1380,7 @@
@Override
public boolean isManagedProfile(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isManagedProfile");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.isManagedProfile();
@@ -1385,19 +1389,20 @@
@Override
public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserUnlockingOrUnlocked");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "isUserUnlockingOrUnlocked");
return mLocalService.isUserUnlockingOrUnlocked(userId);
}
@Override
public boolean isUserUnlocked(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserUnlocked");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isUserUnlocked");
return mLocalService.isUserUnlocked(userId);
}
@Override
public boolean isUserRunning(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserRunning");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isUserRunning");
return mLocalService.isUserRunning(userId);
}
@@ -1437,7 +1442,7 @@
}
}
- private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId,
+ private void checkManageOrInteractPermissionIfCallerInOtherProfileGroup(@UserIdInt int userId,
String name) {
final int callingUserId = UserHandle.getCallingUserId();
if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
@@ -1466,11 +1471,7 @@
@Override
public boolean isPreCreated(@UserIdInt int userId) {
- final int callingUserId = UserHandle.getCallingUserId();
- if (callingUserId != userId && !hasManageUsersPermission()) {
- throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
- + " is pre-created");
- }
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isPreCreated");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.preCreated;
@@ -1826,7 +1827,7 @@
/** @return a specific user restriction that's in effect currently. */
@Override
public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
return mLocalService.hasUserRestriction(restrictionKey, userId);
}
@@ -1951,7 +1952,7 @@
*/
@Override
public Bundle getUserRestrictions(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index d3f668c..0e294f70 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -391,8 +391,9 @@
ArrayMap<String, ProcessInfo> retProcs = new ArrayMap<>(numProcs);
for (String key : procs.keySet()) {
ParsedProcess proc = procs.get(key);
- retProcs.put(proc.getName(), new ProcessInfo(proc.getName(),
- new ArraySet<>(proc.getDeniedPermissions())));
+ retProcs.put(proc.getName(),
+ new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()),
+ proc.getEnableGwpAsan()));
}
return retProcs;
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index 7929579..46b08df 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -25,7 +25,7 @@
import android.content.pm.PermissionGroupInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.parsing.ParsingPackageRead;
-import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.os.Bundle;
@@ -147,7 +147,7 @@
List<ParsedPermissionGroup> getPermissionGroups();
@NonNull
- List<ParsedFeature> getFeatures();
+ List<ParsedAttribution> getAttributions();
/**
* Used to determine the default preferred handler of an {@link Intent}.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 29e5e09..5d6eaf2 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -19,6 +19,8 @@
import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
@@ -53,6 +55,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.IActivityManager;
import android.app.admin.DeviceAdminInfo;
@@ -217,6 +220,9 @@
/** Default permission policy to provide proper behaviour out-of-the-box */
private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
+ /** App ops manager */
+ private final AppOpsManager mAppOpsManager;
+
/**
* Built-in permissions. Read from system configuration files. Mapping is from
* UID to permission name.
@@ -356,6 +362,7 @@
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
mSettings = new PermissionSettings(mLock);
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
@@ -1199,6 +1206,77 @@
}
@Override
+ public boolean setAutoRevokeWhitelisted(
+ @NonNull String packageName, boolean whitelisted, int userId) {
+ Objects.requireNonNull(packageName);
+
+ final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+ final int callingUid = Binder.getCallingUid();
+
+ if (!checkAutoRevokeAccess(pkg, callingUid)) {
+ return false;
+ }
+
+ if (mAppOpsManager
+ .checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
+ callingUid, packageName)
+ != MODE_ALLOWED) {
+ // Whitelist user set - don't override
+ return false;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mAppOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ callingUid, packageName,
+ whitelisted ? MODE_IGNORED : MODE_ALLOWED);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return true;
+ }
+
+ private boolean checkAutoRevokeAccess(AndroidPackage pkg, int callingUid) {
+ if (pkg == null) {
+ return false;
+ }
+
+ final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ final boolean isCallerInstallerOnRecord =
+ mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid);
+
+ if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
+ throw new SecurityException("Caller must either hold "
+ + Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS
+ + " or be the installer on record");
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isAutoRevokeWhitelisted(@NonNull String packageName, int userId) {
+ Objects.requireNonNull(packageName);
+
+ final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+ final int callingUid = Binder.getCallingUid();
+
+ if (!checkAutoRevokeAccess(pkg, callingUid)) {
+ return false;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mAppOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, callingUid, packageName)
+ == MODE_IGNORED;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
@@ -3093,6 +3171,36 @@
}
}
+ @Override
+ public List<String> getAutoRevokeExemptionRequestedPackages(int userId) {
+ mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
+ "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
+
+ List<String> result = new ArrayList<>();
+ mPackageManagerInt.forEachInstalledPackage(pkg -> {
+ if (pkg.isDontAutoRevokePermmissions()) {
+ result.add(pkg.getPackageName());
+ }
+ }, userId);
+
+ return result;
+ }
+
+ @Override
+ public List<String> getAutoRevokeExemptionGrantedPackages(int userId) {
+ mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
+ "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
+
+ List<String> result = new ArrayList<>();
+ mPackageManagerInt.forEachInstalledPackage(pkg -> {
+ if (pkg.isAllowDontAutoRevokePermmissions()) {
+ result.add(pkg.getPackageName());
+ }
+ }, userId);
+
+ return result;
+ }
+
private boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
boolean allowed = false;
final int NP = PackageParser.NEW_PERMISSIONS.length;
@@ -4340,6 +4448,12 @@
packageName, permissions, flags, userId);
}
@Override
+ public void setAutoRevokeWhitelisted(
+ @NonNull String packageName, boolean whitelisted, int userId) {
+ PermissionManagerService.this.setAutoRevokeWhitelisted(
+ packageName, whitelisted, userId);
+ }
+ @Override
public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) {
PermissionManagerService.this
.updatePermissions(packageName, pkg, mDefaultPermissionCallback);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 32ef2ce..356d0ab 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -189,7 +189,9 @@
/** Sets the whitelisted, restricted permissions for the given package. */
public abstract void setWhitelistedRestrictedPermissions(
@NonNull String packageName, @NonNull List<String> permissions,
- @PackageManager.PermissionWhitelistFlags int flags, @NonNull int userId);
+ @PackageManager.PermissionWhitelistFlags int flags, int userId);
+ public abstract void setAutoRevokeWhitelisted(
+ @NonNull String packageName, boolean whitelisted, int userId);
/**
* Update permissions when a package changed.
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index d589353..161f304 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -32,6 +32,7 @@
import android.app.AppOpsManagerInternal;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -173,6 +174,65 @@
} catch (RemoteException doesNotHappen) {
Slog.wtf(LOG_TAG, "Cannot set up app-ops listener");
}
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ intentFilter.addDataScheme("package");
+
+
+ /* TODO ntmyren: enable receiver when test flakes are fixed
+ getContext().registerReceiverAsUser(new BroadcastReceiver() {
+ final List<Integer> mUserSetupUids = new ArrayList<>(200);
+ final Map<UserHandle, PermissionControllerManager> mPermControllerManagers =
+ new HashMap<>();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean hasSetupRun = true;
+ try {
+ hasSetupRun = Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE) != 0;
+ } catch (Settings.SettingNotFoundException e) {
+ // Ignore error, assume setup has run
+ }
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ // If there is no valid package for the given UID, return immediately
+ if (packageManagerInternal.getPackage(uid) == null) {
+ return;
+ }
+
+ if (hasSetupRun) {
+ if (!mUserSetupUids.isEmpty()) {
+ synchronized (mUserSetupUids) {
+ for (int i = mUserSetupUids.size() - 1; i >= 0; i--) {
+ updateUid(mUserSetupUids.get(i));
+ }
+ mUserSetupUids.clear();
+ }
+ }
+ updateUid(uid);
+ } else {
+ synchronized (mUserSetupUids) {
+ if (!mUserSetupUids.contains(uid)) {
+ mUserSetupUids.add(uid);
+ }
+ }
+ }
+ }
+
+ private void updateUid(int uid) {
+ UserHandle user = UserHandle.getUserHandleForUid(uid);
+ PermissionControllerManager manager = mPermControllerManagers.get(user);
+ if (manager == null) {
+ manager = new PermissionControllerManager(
+ getUserContext(getContext(), user), FgThread.getHandler());
+ mPermControllerManagers.put(user, manager);
+ }
+ manager.updateUserSensitiveForApp(uid);
+ }
+ }, UserHandle.ALL, intentFilter, null, null);
+ */
}
/**
@@ -182,7 +242,6 @@
* {@link AppOpsManager#sOpToSwitch share an op} to control the access.
*
* @param permission The permission
- *
* @return The op that controls the access of the permission
*/
private static int getSwitchOp(@NonNull String permission) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1b5cc6a..1e12565 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2139,8 +2139,10 @@
}
// check if user has enabled this operation. SecurityException will be thrown if this app
- // has not been allowed by the user
- final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName);
+ // has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
+ // make sure the usage is logged.
+ final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName,
+ null /* featureId */, "check-add");
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
case AppOpsManager.MODE_IGNORED:
@@ -5202,7 +5204,7 @@
if (dock != null) {
int result = ActivityTaskManager.getService()
.startActivityAsUser(null, mContext.getBasePackageName(),
- mContext.getFeatureId(), dock,
+ mContext.getAttributionTag(), dock,
dock.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0,
ActivityManager.START_FLAG_ONLY_IF_NEEDED,
@@ -5214,7 +5216,7 @@
}
int result = ActivityTaskManager.getService()
.startActivityAsUser(null, mContext.getBasePackageName(),
- mContext.getFeatureId(), mHomeIntent,
+ mContext.getAttributionTag(), mHomeIntent,
mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0,
ActivityManager.START_FLAG_ONLY_IF_NEEDED,
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 294deba..3257b63 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -18,6 +18,8 @@
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
+import static android.os.PowerManagerInternal.MODE_DEVICE_IDLE;
+import static android.os.PowerManagerInternal.MODE_DISPLAY_INACTIVE;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
@@ -42,8 +44,6 @@
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
-import android.hardware.power.Boost;
-import android.hardware.power.Mode;
import android.hardware.power.V1_0.PowerHint;
import android.net.Uri;
import android.os.BatteryManager;
@@ -2975,6 +2975,8 @@
synchronized (mLock) {
if (mDisplayState != state) {
mDisplayState = state;
+ setPowerModeInternal(MODE_DISPLAY_INACTIVE,
+ !Display.isActiveState(state));
if (state == Display.STATE_OFF) {
if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
setHalInteractiveModeLocked(false);
@@ -3297,6 +3299,7 @@
}
mDeviceIdleMode = enabled;
updateWakeLockDisabledStatesLocked();
+ setPowerModeInternal(MODE_DEVICE_IDLE, mDeviceIdleMode || mLightDeviceIdleMode);
}
if (enabled) {
EventLogTags.writeDeviceIdleOnPhase("power");
@@ -3310,6 +3313,7 @@
synchronized (mLock) {
if (mLightDeviceIdleMode != enabled) {
mLightDeviceIdleMode = enabled;
+ setPowerModeInternal(MODE_DEVICE_IDLE, mDeviceIdleMode || mLightDeviceIdleMode);
return true;
}
return false;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index beba106..8b6e9a4 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -16,11 +16,13 @@
package com.android.server.power.batterysaver;
import android.Manifest;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
import android.hardware.power.V1_0.PowerHint;
import android.os.BatteryManager;
import android.os.BatterySaverPolicyConfig;
@@ -35,6 +37,7 @@
import android.util.ArrayMap;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -49,6 +52,7 @@
import java.util.ArrayList;
import java.util.Objects;
+import java.util.Optional;
/**
* Responsible for battery saver mode transition logic.
@@ -112,6 +116,14 @@
*/
private final Plugin[] mPlugins;
+ /**
+ * Package name that will receive an explicit manifest broadcast for
+ * {@link PowerManager#ACTION_POWER_SAVE_MODE_CHANGED}. It's {@code null} if it hasn't been
+ * retrieved yet.
+ */
+ @Nullable
+ private Optional<String> mPowerSaveModeChangedListenerPackage;
+
public static final int REASON_PERCENTAGE_AUTOMATIC_ON = 0;
public static final int REASON_PERCENTAGE_AUTOMATIC_OFF = 1;
public static final int REASON_MANUAL_ON = 2;
@@ -494,6 +506,14 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ // Send the broadcast to a manifest-registered receiver that is specified in the config.
+ if (getPowerSaveModeChangedListenerPackage().isPresent()) {
+ intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
+ .setPackage(getPowerSaveModeChangedListenerPackage().get())
+ .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
// Send internal version that requires signature permission.
intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -508,6 +528,20 @@
}
}
+ private Optional<String> getPowerSaveModeChangedListenerPackage() {
+ if (mPowerSaveModeChangedListenerPackage == null) {
+ String configPowerSaveModeChangedListenerPackage =
+ mContext.getString(R.string.config_powerSaveModeChangedListenerPackage);
+ mPowerSaveModeChangedListenerPackage =
+ LocalServices
+ .getService(PackageManagerInternal.class)
+ .isSystemPackage(configPowerSaveModeChangedListenerPackage)
+ ? Optional.of(configPowerSaveModeChangedListenerPackage)
+ : Optional.empty();
+ }
+ return mPowerSaveModeChangedListenerPackage;
+ }
+
private void updateBatterySavingStats() {
final PowerManager pm = getPowerManager();
if (pm == null) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 42fada1..b50c22e 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -63,6 +63,7 @@
import com.android.server.PackageWatchdog;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
+import com.android.server.pm.ApexManager;
import com.android.server.pm.Installer;
import java.io.File;
@@ -485,6 +486,8 @@
}
latch.countDown();
+
+ destroyCeSnapshotsForExpiredRollbacks(userId);
});
try {
@@ -495,6 +498,15 @@
}
@WorkerThread
+ private void destroyCeSnapshotsForExpiredRollbacks(int userId) {
+ int[] rollbackIds = new int[mRollbacks.size()];
+ for (int i = 0; i < rollbackIds.length; i++) {
+ rollbackIds[i] = mRollbacks.get(i).info.getRollbackId();
+ }
+ ApexManager.getInstance().destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+ }
+
+ @WorkerThread
private void updateRollbackLifetimeDurationInMillis() {
mRollbackLifetimeDurationInMillis = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
diff --git a/services/core/java/com/android/server/security/TEST_MAPPING b/services/core/java/com/android/server/security/TEST_MAPPING
new file mode 100644
index 0000000..9a5e90e
--- /dev/null
+++ b/services/core/java/com/android/server/security/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "ApkVerityTest",
+ "file_patterns": ["VerityUtils\\.java"]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 856a40f..2b793c8 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -81,22 +81,18 @@
/** Returns whether the file has fs-verity enabled. */
public static boolean hasFsverity(@NonNull String filePath) {
- // NB: only measure but not check the actual measurement here. As long as this succeeds,
- // the file is on readable if the measurement can be verified against a trusted key, and
- // this is good enough for installed apps.
- int errno = measureFsverityNative(filePath);
- if (errno != 0) {
- if (errno != OsConstants.ENODATA) {
- Slog.e(TAG, "Failed to measure fs-verity, errno " + errno + ": " + filePath);
- }
+ int retval = statxForFsverityNative(filePath);
+ if (retval < 0) {
+ Slog.e(TAG, "Failed to check whether fs-verity is enabled, errno " + -retval + ": "
+ + filePath);
return false;
}
- return true;
+ return (retval == 1);
}
private static native int enableFsverityNative(@NonNull String filePath,
@NonNull byte[] pkcs7Signature);
- private static native int measureFsverityNative(@NonNull String filePath);
+ private static native int statxForFsverityNative(@NonNull String filePath);
/**
* Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.
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 98579af..fd275d8 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -406,8 +406,8 @@
case FrameworkStatsLog.BATTERY_VOLTAGE:
case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
return pullHealthHal(atomTag, data);
- case FrameworkStatsLog.APP_FEATURES_OPS:
- return pullAppFeaturesOps(atomTag, data);
+ case FrameworkStatsLog.ATTRIBUTED_APP_OPS:
+ return pullAttributedAppOps(atomTag, data);
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
@@ -562,7 +562,7 @@
registerAppsOnExternalStorageInfo();
registerFaceSettings();
registerAppOps();
- registerAppFeaturesOps();
+ registerAttributedAppOps();
registerRuntimeAppOpAccessMessage();
registerNotificationRemoteViews();
registerDangerousPermissionState();
@@ -2888,6 +2888,44 @@
HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
processHistoricalOps(histOps, atomTag, pulledData);
+
+ for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
+ final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
+ final int uid = uidOps.getUid();
+ for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
+ final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
+ for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
+ final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
+
+ StatsEvent.Builder e = StatsEvent.newBuilder();
+ e.setAtomId(atomTag);
+ e.writeInt(uid);
+ e.writeString(packageOps.getPackageName());
+ e.writeInt(op.getLoggingOpCode());
+ e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
+ e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
+ e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
+ e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED));
+ e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED));
+ e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED));
+
+ String perm = AppOpsManager.opToPermission(op.getOpCode());
+ if (perm == null) {
+ e.writeBoolean(false);
+ } else {
+ PermissionInfo permInfo;
+ try {
+ permInfo = mContext.getPackageManager().getPermissionInfo(perm, 0);
+ e.writeBoolean(permInfo.getProtection() == PROTECTION_DANGEROUS);
+ } catch (PackageManager.NameNotFoundException exception) {
+ e.writeBoolean(false);
+ }
+ }
+
+ pulledData.add(e.build());
+ }
+ }
+ }
} catch (Throwable t) {
// TODO: catch exceptions at a more granular level
Slog.e(TAG, "Could not read appops", t);
@@ -2898,8 +2936,8 @@
return StatsManager.PULL_SUCCESS;
}
- private void registerAppFeaturesOps() {
- int tagId = FrameworkStatsLog.APP_FEATURES_OPS;
+ private void registerAttributedAppOps() {
+ int tagId = FrameworkStatsLog.ATTRIBUTED_APP_OPS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
@@ -2908,7 +2946,7 @@
);
}
- int pullAppFeaturesOps(int atomTag, List<StatsEvent> pulledData) {
+ int pullAttributedAppOps(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
try {
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
@@ -2946,7 +2984,7 @@
appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
- return processHistoricalOps(histOps, FrameworkStatsLog.APP_FEATURES_OPS, null);
+ return processHistoricalOps(histOps, FrameworkStatsLog.ATTRIBUTED_APP_OPS, null);
}
int processHistoricalOps(HistoricalOps histOps, int atomTag, List<StatsEvent> pulledData) {
@@ -2956,15 +2994,15 @@
final int uid = uidOps.getUid();
for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
- if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
- for (int featureIdx = 0; featureIdx < packageOps.getFeatureCount();
- featureIdx++) {
- final AppOpsManager.HistoricalFeatureOps featureOps =
- packageOps.getFeatureOpsAt(featureIdx);
- for (int opIdx = 0; opIdx < featureOps.getOpCount(); opIdx++) {
- final AppOpsManager.HistoricalOp op = featureOps.getOpAt(opIdx);
+ if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
+ for (int attributionIdx = 0;
+ attributionIdx < packageOps.getAttributedOpsCount(); attributionIdx++) {
+ final AppOpsManager.AttributedHistoricalOps attributedOps =
+ packageOps.getAttributedOpsAt(attributionIdx);
+ for (int opIdx = 0; opIdx < attributedOps.getOpCount(); opIdx++) {
+ final AppOpsManager.HistoricalOp op = attributedOps.getOpAt(opIdx);
counter += processHistoricalOp(op, atomTag, pulledData, uid,
- packageOps.getPackageName(), featureOps.getFeatureId());
+ packageOps.getPackageName(), attributedOps.getTag());
}
}
} else if (atomTag == FrameworkStatsLog.APP_OPS) {
@@ -2981,18 +3019,19 @@
private int processHistoricalOp(AppOpsManager.HistoricalOp op, int atomTag,
@Nullable List<StatsEvent> pulledData, int uid, String packageName,
- @Nullable String feature) {
- if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
+ @Nullable String attributionTag) {
+ if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
if (pulledData == null) { // this is size estimation call
if (op.getForegroundAccessCount(OP_FLAGS_PULLED) + op.getBackgroundAccessCount(
OP_FLAGS_PULLED) == 0) {
return 0;
} else {
- return 32 + packageName.length() + (feature == null ? 1 : feature.length());
+ return 32 + packageName.length() + (attributionTag == null ? 1
+ : attributionTag.length());
}
} else {
- if (abs((op.getOpCode() + feature + packageName).hashCode() + RANDOM_SEED) % 100
- >= mAppOpsSamplingRate) {
+ if (abs((op.getOpCode() + attributionTag + packageName).hashCode() + RANDOM_SEED)
+ % 100 >= mAppOpsSamplingRate) {
return 0;
}
}
@@ -3002,14 +3041,10 @@
e.setAtomId(atomTag);
e.writeInt(uid);
e.writeString(packageName);
- if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
- e.writeString(feature);
+ if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
+ e.writeString(attributionTag);
}
- if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
- e.writeString(op.getOpName());
- } else {
- e.writeInt(op.getOpCode());
- }
+ e.writeInt(op.getLoggingOpCode());
e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
@@ -3032,7 +3067,7 @@
e.writeBoolean(false);
}
}
- if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) {
+ if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
e.writeInt(mAppOpsSamplingRate);
}
pulledData.add(e.build());
@@ -3055,10 +3090,10 @@
e.writeInt(message.getUid());
e.writeString(message.getPackageName());
e.writeString(message.getOp());
- if (message.getFeatureId() == null) {
+ if (message.getAttributionTag() == null) {
e.writeString("");
} else {
- e.writeString(message.getFeatureId());
+ e.writeString(message.getAttributionTag());
}
e.writeString(message.getMessage());
e.writeInt(message.getSamplingStrategy());
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 84cd4cf..78ef68c 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -664,12 +664,13 @@
@Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
+ long operationId) {
enforceBiometricDialog();
if (mBar != null) {
try {
mBar.showAuthenticationDialog(bundle, receiver, biometricModality,
- requireConfirmation, userId, opPackageName);
+ requireConfirmation, userId, opPackageName, operationId);
} catch (RemoteException ex) {
}
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 8164526..0d16fcc 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -41,6 +41,7 @@
import android.util.SparseArray;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.SystemTextClassifierMetadata;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationConstants;
import android.view.textclassifier.TextClassificationContext;
@@ -179,12 +180,12 @@
TextSelection.Request request, ITextClassifierCallback callback)
throws RemoteException {
Objects.requireNonNull(request);
+ Objects.requireNonNull(request.getSystemTextClassifierMetadata());
handleRequest(
- request.getUserId(),
- request.getCallingPackageName(),
+ request.getSystemTextClassifierMetadata(),
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ true,
- request.getUseDefaultTextClassifier(),
service -> service.onSuggestSelection(sessionId, request, callback),
"onSuggestSelection",
callback);
@@ -196,12 +197,12 @@
TextClassification.Request request, ITextClassifierCallback callback)
throws RemoteException {
Objects.requireNonNull(request);
+ Objects.requireNonNull(request.getSystemTextClassifierMetadata());
handleRequest(
- request.getUserId(),
- request.getCallingPackageName(),
+ request.getSystemTextClassifierMetadata(),
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ true,
- request.getUseDefaultTextClassifier(),
service -> service.onClassifyText(sessionId, request, callback),
"onClassifyText",
callback);
@@ -213,12 +214,12 @@
TextLinks.Request request, ITextClassifierCallback callback)
throws RemoteException {
Objects.requireNonNull(request);
+ Objects.requireNonNull(request.getSystemTextClassifierMetadata());
handleRequest(
- request.getUserId(),
- request.getCallingPackageName(),
+ request.getSystemTextClassifierMetadata(),
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ true,
- request.getUseDefaultTextClassifier(),
service -> service.onGenerateLinks(sessionId, request, callback),
"onGenerateLinks",
callback);
@@ -229,12 +230,12 @@
@Nullable TextClassificationSessionId sessionId, SelectionEvent event)
throws RemoteException {
Objects.requireNonNull(event);
+ Objects.requireNonNull(event.getSystemTextClassifierMetadata());
handleRequest(
- event.getUserId(),
- /* callingPackageName= */ null,
+ event.getSystemTextClassifierMetadata(),
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ false,
- event.getUseDefaultTextClassifier(),
service -> service.onSelectionEvent(sessionId, event),
"onSelectionEvent",
NO_OP_CALLBACK);
@@ -246,18 +247,14 @@
TextClassifierEvent event) throws RemoteException {
Objects.requireNonNull(event);
- final int userId = event.getEventContext() == null
- ? UserHandle.getCallingUserId()
- : event.getEventContext().getUserId();
- final boolean useDefaultTextClassifier =
- event.getEventContext() != null
- ? event.getEventContext().getUseDefaultTextClassifier()
- : true;
+ final TextClassificationContext eventContext = event.getEventContext();
+ final SystemTextClassifierMetadata systemTcMetadata =
+ eventContext != null ? eventContext.getSystemTextClassifierMetadata() : null;
+
handleRequest(
- userId,
- /* callingPackageName= */ null,
+ systemTcMetadata,
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ false,
- useDefaultTextClassifier,
service -> service.onTextClassifierEvent(sessionId, event),
"onTextClassifierEvent",
NO_OP_CALLBACK);
@@ -269,12 +266,12 @@
TextLanguage.Request request,
ITextClassifierCallback callback) throws RemoteException {
Objects.requireNonNull(request);
+ Objects.requireNonNull(request.getSystemTextClassifierMetadata());
handleRequest(
- request.getUserId(),
- request.getCallingPackageName(),
+ request.getSystemTextClassifierMetadata(),
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ true,
- request.getUseDefaultTextClassifier(),
service -> service.onDetectLanguage(sessionId, request, callback),
"onDetectLanguage",
callback);
@@ -286,12 +283,12 @@
ConversationActions.Request request,
ITextClassifierCallback callback) throws RemoteException {
Objects.requireNonNull(request);
+ Objects.requireNonNull(request.getSystemTextClassifierMetadata());
handleRequest(
- request.getUserId(),
- request.getCallingPackageName(),
+ request.getSystemTextClassifierMetadata(),
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ true,
- request.getUseDefaultTextClassifier(),
service -> service.onSuggestConversationActions(sessionId, request, callback),
"onSuggestConversationActions",
callback);
@@ -303,13 +300,12 @@
throws RemoteException {
Objects.requireNonNull(sessionId);
Objects.requireNonNull(classificationContext);
+ Objects.requireNonNull(classificationContext.getSystemTextClassifierMetadata());
- final int userId = classificationContext.getUserId();
handleRequest(
- userId,
- classificationContext.getPackageName(),
+ classificationContext.getSystemTextClassifierMetadata(),
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ false,
- classificationContext.getUseDefaultTextClassifier(),
service -> {
service.onCreateTextClassificationSession(classificationContext, sessionId);
mSessionCache.put(sessionId, classificationContext);
@@ -333,11 +329,13 @@
textClassificationContext != null
? textClassificationContext.useDefaultTextClassifier
: true;
+ final SystemTextClassifierMetadata sysTcMetadata = new SystemTextClassifierMetadata(
+ "", userId, useDefaultTextClassifier);
+
handleRequest(
- userId,
- /* callingPackageName= */ null,
+ sysTcMetadata,
+ /* verifyCallingPackage= */ false,
/* attemptToBind= */ false,
- useDefaultTextClassifier,
service -> {
service.onDestroyTextClassificationSession(sessionId);
mSessionCache.remove(sessionId);
@@ -412,10 +410,9 @@
}
private void handleRequest(
- @UserIdInt int userId,
- @Nullable String callingPackageName,
+ @Nullable SystemTextClassifierMetadata sysTcMetadata,
+ boolean verifyCallingPackage,
boolean attemptToBind,
- boolean useDefaultTextClassifier,
@NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
@NonNull String methodName,
@NonNull ITextClassifierCallback callback) throws RemoteException {
@@ -423,8 +420,17 @@
Objects.requireNonNull(methodName);
Objects.requireNonNull(callback);
+ final int userId =
+ sysTcMetadata == null ? UserHandle.getCallingUserId() : sysTcMetadata.getUserId();
+ final String callingPackageName =
+ sysTcMetadata == null ? null : sysTcMetadata.getCallingPackageName();
+ final boolean useDefaultTextClassifier =
+ sysTcMetadata == null ? true : sysTcMetadata.useDefaultTextClassifier();
+
try {
- validateCallingPackage(callingPackageName);
+ if (verifyCallingPackage) {
+ validateCallingPackage(callingPackageName);
+ }
validateUser(userId);
} catch (Exception e) {
throw new RemoteException("Invalid request: " + e.getMessage(), e,
@@ -636,8 +642,10 @@
public final boolean useDefaultTextClassifier;
StrippedTextClassificationContext(TextClassificationContext textClassificationContext) {
- userId = textClassificationContext.getUserId();
- useDefaultTextClassifier = textClassificationContext.getUseDefaultTextClassifier();
+ SystemTextClassifierMetadata sysTcMetadata =
+ textClassificationContext.getSystemTextClassifierMetadata();
+ userId = sysTcMetadata.getUserId();
+ useDefaultTextClassifier = sysTcMetadata.useDefaultTextClassifier();
}
}
diff --git a/services/core/java/com/android/server/tv/UinputBridge.java b/services/core/java/com/android/server/tv/UinputBridge.java
index 752aa66..a2fe5fc 100644
--- a/services/core/java/com/android/server/tv/UinputBridge.java
+++ b/services/core/java/com/android/server/tv/UinputBridge.java
@@ -28,7 +28,7 @@
public final class UinputBridge {
private final CloseGuard mCloseGuard = CloseGuard.get();
private long mPtr;
- private IBinder mToken = null;
+ private IBinder mToken;
private static native long nativeOpen(String name, String uniqueId, int width, int height,
int maxPointers);
@@ -39,6 +39,25 @@
private static native void nativeSendPointerUp(long ptr, int pointerId);
private static native void nativeSendPointerSync(long ptr);
+ /** Opens a gamepad - will support gamepad key and axis sending */
+ private static native long nativeGamepadOpen(String name, String uniqueId);
+
+ /** Marks the specified key up/down for a gamepad */
+ private static native void nativeSendGamepadKey(long ptr, int keyIndex, boolean down);
+
+ /**
+ * Gamepads pre-define the following axes:
+ * - Left joystick X, axis == ABS_X == 0, range [0, 254]
+ * - Left joystick Y, axis == ABS_Y == 1, range [0, 254]
+ * - Right joystick X, axis == ABS_RX == 3, range [0, 254]
+ * - Right joystick Y, axis == ABS_RY == 4, range [0, 254]
+ * - Left trigger, axis == ABS_Z == 2, range [0, 254]
+ * - Right trigger, axis == ABS_RZ == 5, range [0, 254]
+ * - DPad X, axis == ABS_HAT0X == 0x10, range [-1, 1]
+ * - DPad Y, axis == ABS_HAT0Y == 0x11, range [-1, 1]
+ */
+ private static native void nativeSendGamepadAxisValue(long ptr, int axis, int value);
+
public UinputBridge(IBinder token, String name, int width, int height, int maxPointers)
throws IOException {
if (width < 1 || height < 1) {
@@ -58,12 +77,31 @@
mCloseGuard.open("close");
}
+ /** Constructor used by static factory methods */
+ private UinputBridge(IBinder token, long ptr) {
+ mPtr = ptr;
+ mToken = token;
+ mCloseGuard.open("close");
+ }
+
+ /** Opens a UinputBridge that supports gamepad buttons and axes. */
+ public static UinputBridge openGamepad(IBinder token, String name)
+ throws IOException {
+ if (token == null) {
+ throw new IllegalArgumentException("Token cannot be null");
+ }
+ long ptr = nativeGamepadOpen(name, token.toString());
+ if (ptr == 0) {
+ throw new IOException("Could not open uinput device " + name);
+ }
+
+ return new UinputBridge(token, ptr);
+ }
+
@Override
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
+ mCloseGuard.warnIfOpen();
close(mToken);
} finally {
mToken = null;
@@ -119,7 +157,35 @@
if (isTokenValid(token)) {
nativeSendPointerSync(mPtr);
}
+ }
+ /** Send a gamepad key
+ * @param keyIndex - the index of the w3-spec key
+ * @param down - is the key pressed ?
+ */
+ public void sendGamepadKey(IBinder token, int keyIndex, boolean down) {
+ if (isTokenValid(token)) {
+ nativeSendGamepadKey(mPtr, keyIndex, down);
+ }
+ }
+
+ /** Send a gamepad axis value.
+ * - Left joystick X, axis == ABS_X == 0, range [0, 254]
+ * - Left joystick Y, axis == ABS_Y == 1, range [0, 254]
+ * - Right joystick X, axis == ABS_RX == 3, range [0, 254]
+ * - Right joystick Y, axis == ABS_RY == 4, range [0, 254]
+ * - Left trigger, axis == ABS_Z == 2, range [0, 254]
+ * - Right trigger, axis == ABS_RZ == 5, range [0, 254]
+ * - DPad X, axis == ABS_HAT0X == 0x10, range [-1, 1]
+ * - DPad Y, axis == ABS_HAT0Y == 0x11, range [-1, 1]
+ *
+ * @param axis is the axis index
+ * @param value is the value to set for that axis
+ */
+ public void sendGamepadAxisValue(IBinder token, int axis, int value) {
+ if (isTokenValid(token)) {
+ nativeSendGamepadAxisValue(mPtr, axis, value);
+ }
}
public void clear(IBinder token) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 04d551d..25585b3 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -24,6 +24,8 @@
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
@@ -68,6 +70,9 @@
private TvInputManager mManager;
private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
+ // An internal resource request count to help generate resource handle.
+ private int mResourceRequestCount = 0;
+
// Used to synchronize the access to the service.
private final Object mLock = new Object();
@@ -94,7 +99,7 @@
public void registerClientProfile(@NonNull ResourceClientProfile profile,
@NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)
throws RemoteException {
- enforceAccessPermission();
+ enforceTrmAccessPermission("registerClientProfile");
if (profile == null) {
throw new RemoteException("ResourceClientProfile can't be null");
}
@@ -118,7 +123,7 @@
@Override
public void unregisterClientProfile(int clientId) throws RemoteException {
- enforceAccessPermission();
+ enforceTrmAccessPermission("unregisterClientProfile");
synchronized (mLock) {
if (!checkClientExists(clientId)) {
Slog.e(TAG, "Unregistering non exists client:" + clientId);
@@ -130,7 +135,7 @@
@Override
public boolean updateClientPriority(int clientId, int priority, int niceValue) {
- enforceAccessPermission();
+ enforceTrmAccessPermission("updateClientPriority");
synchronized (mLock) {
return updateClientPriorityInternal(clientId, priority, niceValue);
}
@@ -138,7 +143,7 @@
@Override
public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException {
- enforceAccessPermission();
+ enforceTrmAccessPermission("setFrontendInfoList");
if (infos == null) {
throw new RemoteException("TunerFrontendInfo can't be null");
}
@@ -149,6 +154,7 @@
@Override
public void updateCasInfo(int casSystemId, int maxSessionNum) {
+ enforceTrmAccessPermission("updateCasInfo");
if (DEBUG) {
Slog.d(TAG,
"updateCasInfo(casSystemId=" + casSystemId
@@ -158,6 +164,7 @@
@Override
public void setLnbInfoList(int[] lnbIds) {
+ enforceTrmAccessPermission("setLnbInfoList");
if (DEBUG) {
for (int i = 0; i < lnbIds.length; i++) {
Slog.d(TAG, "updateLnbInfo(lnbId=" + lnbIds[i] + ")");
@@ -167,14 +174,15 @@
@Override
public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @NonNull int[] frontendId) throws RemoteException {
- enforceAccessPermission();
- if (frontendId == null) {
- throw new RemoteException("frontendId can't be null");
+ @NonNull int[] frontendHandle) throws RemoteException {
+ enforceTunerAccessPermission("requestFrontend");
+ enforceTrmAccessPermission("requestFrontend");
+ if (frontendHandle == null) {
+ throw new RemoteException("frontendHandle can't be null");
}
synchronized (mLock) {
try {
- return requestFrontendInternal(request, frontendId);
+ return requestFrontendInternal(request, frontendHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -183,14 +191,43 @@
@Override
public void shareFrontend(int selfClientId, int targetClientId) {
+ enforceTunerAccessPermission("shareFrontend");
+ enforceTrmAccessPermission("shareFrontend");
if (DEBUG) {
Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId);
}
}
@Override
+ public boolean requestDemux(@NonNull TunerDemuxRequest request,
+ @NonNull int[] demuxHandle) throws RemoteException {
+ enforceTunerAccessPermission("requestDemux");
+ enforceTrmAccessPermission("requestDemux");
+ if (demuxHandle == null) {
+ throw new RemoteException("demuxHandle can't be null");
+ }
+ synchronized (mLock) {
+ return requestDemuxInternal(request, demuxHandle);
+ }
+ }
+
+ @Override
+ public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
+ @NonNull int[] descrambleHandle) throws RemoteException {
+ enforceDescramblerAccessPermission("requestDescrambler");
+ enforceTrmAccessPermission("requestDescrambler");
+ if (descrambleHandle == null) {
+ throw new RemoteException("descrambleHandle can't be null");
+ }
+ synchronized (mLock) {
+ return requestDescramblerInternal(request, descrambleHandle);
+ }
+ }
+
+ @Override
public boolean requestCasSession(
- @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) {
+ @NonNull CasSessionRequest request, @NonNull int[] sessionResourceHandle) {
+ enforceTrmAccessPermission("requestCasSession");
if (DEBUG) {
Slog.d(TAG, "requestCasSession(request=" + request + ")");
}
@@ -199,7 +236,9 @@
}
@Override
- public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbId) {
+ public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) {
+ enforceTunerAccessPermission("requestLnb");
+ enforceTrmAccessPermission("requestLnb");
if (DEBUG) {
Slog.d(TAG, "requestLnb(request=" + request + ")");
}
@@ -208,13 +247,34 @@
@Override
public void releaseFrontend(int frontendId) {
+ enforceTunerAccessPermission("releaseFrontend");
+ enforceTrmAccessPermission("releaseFrontend");
if (DEBUG) {
Slog.d(TAG, "releaseFrontend(id=" + frontendId + ")");
}
}
@Override
+ public void releaseDemux(int demuxHandle) {
+ enforceTunerAccessPermission("releaseDemux");
+ enforceTrmAccessPermission("releaseDemux");
+ if (DEBUG) {
+ Slog.d(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")");
+ }
+ }
+
+ @Override
+ public void releaseDescrambler(int descramblerHandle) {
+ enforceTunerAccessPermission("releaseDescrambler");
+ enforceTrmAccessPermission("releaseDescrambler");
+ if (DEBUG) {
+ Slog.d(TAG, "releaseDescrambler(descramblerHandle=" + descramblerHandle + ")");
+ }
+ }
+
+ @Override
public void releaseCasSession(int sessionResourceId) {
+ enforceTrmAccessPermission("releaseCasSession");
if (DEBUG) {
Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")");
}
@@ -222,6 +282,8 @@
@Override
public void releaseLnb(int lnbId) {
+ enforceTunerAccessPermission("releaseLnb");
+ enforceTrmAccessPermission("releaseLnb");
if (DEBUG) {
Slog.d(TAG, "releaseLnb(lnbId=" + lnbId + ")");
}
@@ -230,6 +292,7 @@
@Override
public boolean isHigherPriority(
ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) {
+ enforceTrmAccessPermission("isHigherPriority");
if (DEBUG) {
Slog.d(TAG,
"isHigherPriority(challengerProfile=" + challengerProfile
@@ -337,13 +400,13 @@
}
@VisibleForTesting
- protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId)
+ protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle)
throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestFrontend(request=" + request + ")");
}
- frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
+ frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
if (!checkClientExists(request.getClientId())) {
Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId());
return false;
@@ -379,17 +442,20 @@
// Grant frontend when there is unused resource.
if (grantingFrontendId > -1) {
- frontendId[0] = grantingFrontendId;
- updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+ frontendHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, grantingFrontendId);
+ updateFrontendClientMappingOnNewGrant(grantingFrontendId, request.getClientId());
return true;
}
// When all the resources are occupied, grant the lowest priority resource if the
// request client has higher priority.
if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
- frontendId[0] = inUseLowestPriorityFrId;
- reclaimFrontendResource(getFrontendResource(frontendId[0]).getOwnerClientId());
- updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+ frontendHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, inUseLowestPriorityFrId);
+ reclaimFrontendResource(getFrontendResource(
+ inUseLowestPriorityFrId).getOwnerClientId());
+ updateFrontendClientMappingOnNewGrant(inUseLowestPriorityFrId, request.getClientId());
return true;
}
@@ -397,6 +463,25 @@
}
@VisibleForTesting
+ boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
+ if (DEBUG) {
+ Slog.d(TAG, "requestDemux(request=" + request + ")");
+ }
+ demuxHandle[0] = generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
+ return true;
+ }
+
+ @VisibleForTesting
+ boolean requestDescramblerInternal(TunerDescramblerRequest request, int[] descramblerHandle) {
+ if (DEBUG) {
+ Slog.d(TAG, "requestDescrambler(request=" + request + ")");
+ }
+ descramblerHandle[0] =
+ generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER, 0);
+ return true;
+ }
+
+ @VisibleForTesting
protected class ResourcesReclaimListenerRecord implements IBinder.DeathRecipient {
private final IResourcesReclaimListener mListener;
private final int mClientId;
@@ -558,8 +643,25 @@
return mClientProfiles.keySet().contains(clientId);
}
- private void enforceAccessPermission() {
- getContext().enforceCallingOrSelfPermission(
- "android.permission.TUNER_RESOURCE_ACCESS", TAG);
+ private int generateResourceHandle(
+ @TunerResourceManager.TunerResourceType int resourceType, int resourceId) {
+ return (resourceType & 0x000000ff) << 24
+ | (resourceId << 16)
+ | (mResourceRequestCount++ & 0xffff);
+ }
+
+ private void enforceTrmAccessPermission(String apiName) {
+ getContext().enforceCallingPermission("android.permission.TUNER_RESOURCE_ACCESS",
+ TAG + ": " + "apiName");
+ }
+
+ private void enforceTunerAccessPermission(String apiName) {
+ getContext().enforceCallingPermission("android.Manifest.permission.ACCESS_TV_TUNER",
+ TAG + ": " + "apiName");
+ }
+
+ private void enforceDescramblerAccessPermission(String apiName) {
+ getContext().enforceCallingPermission("android.Manifest.permission.ACCESS_TV_DESCRAMBLER",
+ TAG + ": " + "apiName");
}
}
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index 761fbf8..88a60dd 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -50,7 +50,7 @@
implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener {
private static final String TAG = "TwilightService";
- private static final String FEATURE_ID = "TwilightService";
+ private static final String ATTRIBUTION_TAG = "TwilightService";
private static final boolean DEBUG = false;
private static final int MSG_START_LISTENING = 1;
@@ -74,7 +74,7 @@
protected TwilightState mLastTwilightState;
public TwilightService(Context context) {
- super(context.createFeatureContext(FEATURE_ID));
+ super(context.createAttributionContext(ATTRIBUTION_TAG));
mHandler = new Handler(Looper.getMainLooper(), this);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6eb3c0f..a298b89 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2184,47 +2184,6 @@
}
}
- /**
- * Called when the wallpaper needs to zoom out.
- *
- * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in.
- * @param callingPackage package name calling this API.
- * @param displayId id of the display whose zoom is updating.
- */
- public void setWallpaperZoomOut(float zoom, String callingPackage, int displayId) {
- if (!isWallpaperSupported(callingPackage)) {
- return;
- }
- synchronized (mLock) {
- if (!isValidDisplay(displayId)) {
- throw new IllegalArgumentException("Cannot find display with id=" + displayId);
- }
- int userId = UserHandle.getCallingUserId();
- if (mCurrentUserId != userId) {
- return; // Don't change the properties now
- }
- WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
- if (zoom < 0 || zoom > 1f) {
- throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom);
- }
-
- if (wallpaper.connection != null) {
- final WallpaperConnection.DisplayConnector connector = wallpaper.connection
- .getDisplayConnectorOrCreate(displayId);
- final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
- if (engine != null) {
- try {
- engine.setZoomOut(zoom);
- } catch (RemoteException e) {
- if (DEBUG) {
- Slog.w(TAG, "Couldn't set wallpaper zoom", e);
- }
- }
- }
- }
- }
- }
-
@Deprecated
@Override
public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b6ad241..00c6f3a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1351,11 +1351,16 @@
mLetterbox.attachInput(w);
}
getPosition(mTmpPoint);
- // Get the bounds of the "space-to-fill". In multi-window mode, the task-level
- // represents this. In fullscreen-mode, the stack does (since the orientation letterbox
- // is also applied to the task).
- Rect spaceToFill = (inMultiWindowMode() || getStack() == null)
- ? task.getDisplayedBounds() : getStack().getDisplayedBounds();
+ // Get the bounds of the "space-to-fill". The transformed bounds have the highest
+ // priority because the activity is launched in a rotated environment. In multi-window
+ // mode, the task-level represents this. In fullscreen-mode, the task container does
+ // (since the orientation letterbox is also applied to the task).
+ final Rect transformedBounds = getFixedRotationTransformDisplayBounds();
+ final Rect spaceToFill = transformedBounds != null
+ ? transformedBounds
+ : inMultiWindowMode()
+ ? task.getDisplayedBounds()
+ : getRootTask().getParent().getDisplayedBounds();
mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
@@ -1589,6 +1594,11 @@
info.taskAffinity = uid + ":" + info.taskAffinity;
}
taskAffinity = info.taskAffinity;
+ if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null
+ && !info.windowLayout.windowLayoutAffinity.startsWith(uid)) {
+ info.windowLayout.windowLayoutAffinity =
+ uid + ":" + info.windowLayout.windowLayoutAffinity;
+ }
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;
@@ -5926,7 +5936,7 @@
if (win == null) {
return;
}
- final Rect frame = win.getFrameLw();
+ final Rect frame = win.getRelativeFrameLw();
final int thumbnailDrawableRes = task.mUserId == mWmService.mCurrentUserId
? R.drawable.ic_account_circle
: R.drawable.ic_corp_badge;
@@ -5936,12 +5946,12 @@
if (thumbnail == null) {
return;
}
- final Transaction transaction = getAnimatingContainer().getPendingTransaction();
+ final Transaction transaction = getPendingTransaction();
mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory,
- transaction, getAnimatingContainer(), thumbnail);
+ transaction, getTask(), thumbnail);
final Animation animation =
getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
- win.getFrameLw());
+ frame);
mThumbnail.startAnimation(transaction, animation, new Point(frame.left, frame.top));
}
@@ -6357,33 +6367,17 @@
if (mCompatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration);
} else {
- // We ignore activities' requested orientation in multi-window modes. Task level may
- // take them into consideration when calculating bounds.
if (inMultiWindowMode()) {
+ // We ignore activities' requested orientation in multi-window modes. Task level may
+ // take them into consideration when calculating bounds.
resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED;
- }
- final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
- final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
- // Use tmp bounds to calculate aspect ratio so we can know whether the activity should
- // use restricted size (resolvedBounds may be the requested override bounds).
- mTmpBounds.setEmpty();
- applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds);
- // If the out bounds is not empty, it means the activity cannot fill parent's app
- // bounds, then the relative configuration (e.g. screen size, layout) needs to be
- // resolved according to the bounds.
- if (!mTmpBounds.isEmpty()) {
- final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- resolvedBounds.set(mTmpBounds);
- // Exclude the horizontal decor area because the activity will be centered
- // horizontally in parent's app bounds to balance the visual appearance.
- resolvedBounds.left = parentAppBounds.left;
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
- getFixedRotationTransformDisplayInfo());
- final int offsetX = getHorizontalCenterOffset(
- parentAppBounds.width(), resolvedBounds.width());
- if (offsetX > 0) {
- offsetBounds(resolvedConfig, offsetX - resolvedBounds.left, 0 /* offsetY */);
+ // If the activity has requested override bounds, the configuration needs to be
+ // computed accordingly.
+ if (!matchParentBounds()) {
+ task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
+ } else {
+ resolveFullscreenConfiguration(newParentConfiguration);
}
}
@@ -6395,6 +6389,43 @@
}
/**
+ * Resolves the configuration of activity in fullscreen mode. If the bounds are restricted by
+ * aspect ratio, the position will be centered horizontally in parent's app bounds to balance
+ * the visual appearance. The policy of aspect ratio has higher priority than the requested
+ * override bounds.
+ */
+ private void resolveFullscreenConfiguration(Configuration newParentConfiguration) {
+ final Configuration resolvedConfig = getResolvedOverrideConfiguration();
+ final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+ final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+ // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
+ // restricted size (resolved bounds may be the requested override bounds).
+ mTmpBounds.setEmpty();
+ applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds);
+ // If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
+ // then there is space to be centered.
+ final boolean needToBeCentered = !mTmpBounds.isEmpty();
+ if (needToBeCentered) {
+ resolvedBounds.set(mTmpBounds);
+ // Exclude the horizontal decor area.
+ resolvedBounds.left = parentAppBounds.left;
+ }
+ if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
+ // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
+ // restrict, the bounds should be the requested override bounds.
+ task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+ getFixedRotationTransformDisplayInfo());
+ }
+ if (needToBeCentered) {
+ // Offset to center relative to parent's app bounds.
+ final int offsetX = getHorizontalCenterOffset(
+ parentAppBounds.width(), resolvedBounds.width());
+ offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */);
+ }
+ }
+
+ /**
* Resolves consistent screen configuration for orientation and rotation changes without
* inheriting the parent bounds.
*/
@@ -6502,7 +6533,7 @@
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
final int screenPosX = parentAppBounds.left + offsetX;
final int screenPosY = parentBounds.top;
- if (screenPosX > 0 || screenPosY > 0) {
+ if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
mSizeCompatBounds.offset(screenPosX, screenPosY);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 9bad799..e8bfe8e 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -153,7 +153,6 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.ITaskOrganizer;
import android.view.SurfaceControl;
import com.android.internal.annotations.GuardedBy;
@@ -307,8 +306,6 @@
// TODO(task-hierarchy): remove when tiles can be actual parents
TaskTile mTile = null;
- private int mLastTaskOrganizerWindowingMode = -1;
-
private final Handler mHandler;
private class ActivityStackHandler extends Handler {
@@ -635,8 +632,6 @@
super.onConfigurationChanged(newParentConfig);
- updateTaskOrganizerState();
-
// Only need to update surface size here since the super method will handle updating
// surface position.
updateSurfaceSize(getPendingTransaction());
@@ -692,30 +687,6 @@
}
}
- void updateTaskOrganizerState() {
- if (!isRootTask()) {
- return;
- }
-
- final int windowingMode = getWindowingMode();
- if (windowingMode == mLastTaskOrganizerWindowingMode) {
- // If our windowing mode hasn't actually changed, then just stick
- // with our old organizer. This lets us implement the semantic
- // where SysUI can continue to manage it's old tasks
- // while CTS temporarily takes over the registration.
- return;
- }
- /*
- * Different windowing modes may be managed by different task organizers. If
- * getTaskOrganizer returns null, we still call setTaskOrganizer to
- * make sure we clear it.
- */
- final ITaskOrganizer org =
- mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
- setTaskOrganizer(org);
- mLastTaskOrganizerWindowingMode = windowingMode;
- }
-
@Override
public void setWindowingMode(int windowingMode) {
// Calling Task#setWindowingMode() for leaf task since this is the a specialization of
@@ -1539,7 +1510,7 @@
*/
@StackVisibility
int getVisibility(ActivityRecord starting) {
- if (!isAttached() || mForceHidden) {
+ if (!isAttached() || isForceHidden()) {
return STACK_VISIBILITY_INVISIBLE;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 6d7f8fb..57f357d 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -75,6 +75,7 @@
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.RootWindowContainer.TAG_STATES;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
@@ -1565,9 +1566,9 @@
* stopping list by handling the idle.
*/
stack.cancelAnimation();
- stack.mForceHidden = true;
+ stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
- stack.mForceHidden = false;
+ stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
true /* processPausingActivities */, null /* configuration */);
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 1009771..57b6024 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -178,7 +178,7 @@
// before issuing the work challenge.
return true;
}
- return interceptWorkProfileChallengeIfNeeded();
+ return interceptLockedManagedProfileIfNeeded();
}
private boolean hasCrossProfileAnimation() {
@@ -296,7 +296,7 @@
return true;
}
- private boolean interceptWorkProfileChallengeIfNeeded() {
+ private boolean interceptLockedManagedProfileIfNeeded() {
final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
if (interceptingIntent == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 36fdb2d..aad242d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -375,7 +375,7 @@
*/
final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
- /** @see #computeCompatSmallestWidth(boolean, int, int, int, DisplayCutout) */
+ /** @see #computeCompatSmallestWidth(boolean, int, int, int) */
private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
/**
@@ -1814,7 +1814,7 @@
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw,
- dh, displayCutout);
+ dh);
}
/**
@@ -1922,8 +1922,7 @@
mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}
- private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
- DisplayCutout displayCutout) {
+ private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh) {
mTmpDisplayMetrics.setTo(mDisplayMetrics);
final DisplayMetrics tmpDm = mTmpDisplayMetrics;
final int unrotDw, unrotDh;
@@ -1934,19 +1933,21 @@
unrotDw = dw;
unrotDh = dh;
}
- int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
- displayCutout);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
- displayCutout);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
- displayCutout);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
- displayCutout);
+ int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw,
+ unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh,
+ unrotDw);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw,
+ unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh,
+ unrotDw);
return sw;
}
private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
- DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout) {
+ DisplayMetrics dm, int dw, int dh) {
+ final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
+ rotation).getDisplayCutout();
dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
displayCutout);
dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
@@ -1987,20 +1988,20 @@
return;
}
int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
- displayInfo.displayCutout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
- displayInfo.displayCutout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
- displayInfo.displayCutout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
- displayInfo.displayCutout);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);
outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
outConfig.screenLayout = sl;
}
private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
- int uiMode, DisplayCutout displayCutout) {
+ int uiMode) {
+ // Get the display cutout at this rotation.
+ final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
+ rotation).getDisplayCutout();
+
// Get the app screen size at this rotation.
int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
@@ -2224,9 +2225,7 @@
.addTaggedData(MetricsEvent.FIELD_DISPLAY_ID, getDisplayId()));
}
- // If there was no pinned stack, we still need to notify the controller of the display info
- // update as a result of the config change.
- if (mPinnedStackControllerLocked != null && !hasPinnedTask()) {
+ if (mPinnedStackControllerLocked != null) {
mPinnedStackControllerLocked.onDisplayInfoChanged(getDisplayInfo());
}
}
@@ -6525,6 +6524,7 @@
lastReparentedStack.postReparent();
}
releaseSelfIfNeeded();
+ mDisplayPolicy.release();
if (!mAllSleepTokens.isEmpty()) {
mRootWindowContainer.mSleepTokens.removeAll(mAllSleepTokens);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a094c81..38a406e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -28,6 +28,7 @@
import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
@@ -179,6 +180,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.function.TriConsumer;
@@ -259,7 +261,9 @@
@Px
private int mBottomGestureAdditionalInset;
@Px
- private int mSideGestureInset;
+ private int mLeftGestureInset;
+ @Px
+ private int mRightGestureInset;
StatusBarManagerInternal getStatusBarManagerInternal() {
synchronized (mServiceAcquireLock) {
@@ -427,6 +431,8 @@
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
+ private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+
private class PolicyHandler extends Handler {
PolicyHandler(Looper looper) {
@@ -440,10 +446,12 @@
updateDreamingSleepToken(msg.arg1 != 0);
break;
case MSG_REQUEST_TRANSIENT_BARS:
- WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
- ? mStatusBar : mNavigationBar;
- if (targetBar != null) {
- requestTransientBars(targetBar);
+ synchronized (mLock) {
+ WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
+ ? mStatusBar : mNavigationBar;
+ if (targetBar != null) {
+ requestTransientBars(targetBar);
+ }
}
break;
case MSG_DISPOSE_INPUT_CONSUMER:
@@ -499,15 +507,20 @@
new SystemGesturesPointerEventListener.Callbacks() {
@Override
public void onSwipeFromTop() {
- if (mStatusBar != null) {
- requestTransientBars(mStatusBar);
+ synchronized (mLock) {
+ if (mStatusBar != null) {
+ requestTransientBars(mStatusBar);
+ }
}
}
@Override
public void onSwipeFromBottom() {
- if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
- requestTransientBars(mNavigationBar);
+ synchronized (mLock) {
+ if (mNavigationBar != null
+ && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ requestTransientBars(mNavigationBar);
+ }
}
}
@@ -517,12 +530,13 @@
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
- }
- final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
- || mNavigationBarPosition == NAV_BAR_RIGHT;
- if (mNavigationBar != null && sideAllowed
- && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) {
- requestTransientBars(mNavigationBar);
+ final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
+ || mNavigationBarPosition == NAV_BAR_RIGHT;
+ if (mNavigationBar != null && sideAllowed
+ && !mSystemGestures.currentGestureStartedInRegion(
+ excludedRegion)) {
+ requestTransientBars(mNavigationBar);
+ }
}
excludedRegion.recycle();
}
@@ -533,12 +547,13 @@
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
- }
- final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
- || mNavigationBarPosition == NAV_BAR_LEFT;
- if (mNavigationBar != null && sideAllowed
- && !mSystemGestures.currentGestureStartedInRegion(excludedRegion)) {
- requestTransientBars(mNavigationBar);
+ final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture
+ || mNavigationBarPosition == NAV_BAR_LEFT;
+ if (mNavigationBar != null && sideAllowed
+ && !mSystemGestures.currentGestureStartedInRegion(
+ excludedRegion)) {
+ requestTransientBars(mNavigationBar);
+ }
}
excludedRegion.recycle();
}
@@ -642,6 +657,16 @@
mRefreshRatePolicy = new RefreshRatePolicy(mService,
mDisplayContent.getDisplayInfo(),
mService.mHighRefreshRateBlacklist);
+
+ mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler,
+ mContext, () -> {
+ synchronized (mLock) {
+ onConfigurationChanged();
+ mSystemGestures.onConfigurationChanged();
+ mDisplayContent.updateSystemGestureExclusion();
+ }
+ });
+ mHandler.post(mGestureNavigationSettingsObserver::register);
}
void systemReady() {
@@ -714,7 +739,7 @@
}
boolean hasSideGestures() {
- return mHasNavigationBar && mSideGestureInset > 0;
+ return mHasNavigationBar && (mLeftGestureInset > 0 || mRightGestureInset > 0);
}
public boolean navigationBarCanMove() {
@@ -1000,6 +1025,13 @@
case TYPE_STATUS_BAR_PANEL:
return WindowManagerGlobal.ADD_INVALID_TYPE;
}
+
+ if (attrs.providesInsetsTypes != null) {
+ mContext.enforcePermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ "DisplayPolicy");
+ enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
+ }
return ADD_OKAY;
}
@@ -1067,11 +1099,12 @@
inOutFrame.left = 0;
inOutFrame.top = 0;
inOutFrame.bottom = displayFrames.mDisplayHeight;
- inOutFrame.right = displayFrames.mUnrestricted.left + mSideGestureInset;
+ inOutFrame.right = displayFrames.mUnrestricted.left + mLeftGestureInset;
});
mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win,
(displayFrames, windowState, inOutFrame) -> {
- inOutFrame.left = displayFrames.mUnrestricted.right - mSideGestureInset;
+ inOutFrame.left = displayFrames.mUnrestricted.right
+ - mRightGestureInset;
inOutFrame.top = 0;
inOutFrame.bottom = displayFrames.mDisplayHeight;
inOutFrame.right = displayFrames.mDisplayWidth;
@@ -1085,6 +1118,28 @@
});
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
+ default:
+ if (attrs.providesInsetsTypes != null) {
+ for (int insetsType : attrs.providesInsetsTypes) {
+ mDisplayContent.setInsetProvider(insetsType, win, null);
+ }
+ }
+ break;
+ }
+ }
+
+ private static void enforceSingleInsetsTypeCorrespondingToWindowType(int[] insetsTypes) {
+ int count = 0;
+ for (int insetsType : insetsTypes) {
+ switch (insetsType) {
+ case ITYPE_NAVIGATION_BAR:
+ case ITYPE_STATUS_BAR:
+ case ITYPE_CAPTION_BAR:
+ if (++count > 1) {
+ throw new IllegalArgumentException(
+ "Multiple InsetsTypes corresponding to Window type");
+ }
+ }
}
}
@@ -2810,7 +2865,8 @@
}
mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode);
- mSideGestureInset = res.getDimensionPixelSize(R.dimen.config_backGestureInset);
+ mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
+ mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res);
mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough);
mNavigationBarAlwaysShowOnSideGesture =
res.getBoolean(R.bool.config_navBarAlwaysShowOnSideEdgeGesture);
@@ -3156,47 +3212,46 @@
}
private void requestTransientBars(WindowState swipeTarget) {
- synchronized (mLock) {
- if (!mService.mPolicy.isUserSetupComplete()) {
- // Swipe-up for navigation bar is disabled during setup
+ if (!mService.mPolicy.isUserSetupComplete()) {
+ // Swipe-up for navigation bar is disabled during setup
+ return;
+ }
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+ if (swipeTarget == mNavigationBar
+ && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
+ // Don't show status bar when swiping on already visible navigation bar
return;
}
- if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- if (swipeTarget == mNavigationBar
- && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
- // Don't show status bar when swiping on already visible navigation bar
- return;
- }
- final InsetsControlTarget controlTarget =
- swipeTarget.getControllableInsetProvider().getControlTarget();
+ final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider();
+ final InsetsControlTarget controlTarget = provider != null
+ ? provider.getControlTarget() : null;
- // No transient mode on lockscreen (in notification shade window).
- if (controlTarget == null || controlTarget == getNotificationShade()) {
+ // No transient mode on lockscreen (in notification shade window).
+ if (controlTarget == null || controlTarget == getNotificationShade()) {
+ return;
+ }
+ if (controlTarget.canShowTransient()) {
+ mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+ new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
+ } else {
+ controlTarget.showInsets(Type.systemBars(), false);
+ }
+ } else {
+ boolean sb = mStatusBarController.checkShowTransientBarLw();
+ boolean nb = mNavigationBarController.checkShowTransientBarLw()
+ && !isNavBarEmpty(mLastSystemUiFlags);
+ if (sb || nb) {
+ // Don't show status bar when swiping on already visible navigation bar
+ if (!nb && swipeTarget == mNavigationBar) {
+ if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
return;
}
- if (controlTarget.canShowTransient()) {
- mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
- new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
- } else {
- controlTarget.showInsets(Type.systemBars(), false);
- }
- } else {
- boolean sb = mStatusBarController.checkShowTransientBarLw();
- boolean nb = mNavigationBarController.checkShowTransientBarLw()
- && !isNavBarEmpty(mLastSystemUiFlags);
- if (sb || nb) {
- // Don't show status bar when swiping on already visible navigation bar
- if (!nb && swipeTarget == mNavigationBar) {
- if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
- return;
- }
- if (sb) mStatusBarController.showTransient();
- if (nb) mNavigationBarController.showTransient();
- updateSystemUiVisibilityLw();
- }
+ if (sb) mStatusBarController.showTransient();
+ if (nb) mNavigationBarController.showTransient();
+ updateSystemUiVisibilityLw();
}
- mImmersiveModeConfirmation.confirmCurrentPrompt();
}
+ mImmersiveModeConfirmation.confirmCurrentPrompt();
}
private void disposeInputConsumer(InputConsumer inputConsumer) {
@@ -3945,6 +4000,10 @@
return false;
}
+ void release() {
+ mHandler.post(mGestureNavigationSettingsObserver::unregister);
+ }
+
@VisibleForTesting
static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) {
if (navBarWindow == null || !navBarWindow.isVisibleLw()
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 01f9888..30912e5 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -36,6 +36,7 @@
import android.util.SparseArray;
import android.view.InsetsAnimationControlCallbacks;
import android.view.InsetsAnimationControlImpl;
+import android.view.InsetsAnimationControlRunner;
import android.view.InsetsController;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -44,6 +45,7 @@
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.ViewRootImpl;
import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
import com.android.internal.annotations.VisibleForTesting;
@@ -327,7 +329,7 @@
InsetsPolicyAnimationControlCallbacks mControlCallbacks;
InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) {
- super(show);
+ super(show, true /* useSfVsync */);
mFinishCallback = finishCallback;
mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
}
@@ -360,8 +362,6 @@
mFocusedWin.getDisplayContent().getBounds(), getState(),
mListener, typesReady, this, mListener.getDurationMs(),
InsetsController.INTERPOLATOR, true,
- show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
- : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE);
SurfaceAnimationThread.getHandler().post(
() -> mListener.onReady(mAnimationControl, typesReady));
@@ -377,7 +377,7 @@
}
@Override
- public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
+ public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
// Nothing's needed here. Finish steps is handled in the listener
// onAnimationFinished callback.
}
@@ -406,14 +406,21 @@
applyParams(t, surfaceParams, mTmpFloat9);
}
t.apply();
+ t.close();
+ }
+
+ // Since we don't push applySurfaceParams to a Handler-queue we don't need
+ // to push release in this case.
+ @Override
+ public void releaseSurfaceControlFromRt(SurfaceControl sc) {
+ sc.release();
}
@Override
public void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation,
- WindowInsetsAnimation.Bounds bounds,
- int layoutDuringAnimation) {
+ Bounds bounds) {
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 6ff029b..ee36db9 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_INVALID;
@@ -29,6 +30,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -74,30 +77,28 @@
/**
* When dispatching window state to the client, we'll need to exclude the source that represents
- * the window that is being dispatched.
+ * the window that is being dispatched. We also need to exclude certain types of insets source
+ * for client within specific windowing modes.
*
* @param target The client we dispatch the state to.
* @return The state stripped of the necessary information.
*/
InsetsState getInsetsForDispatch(@NonNull WindowState target) {
final InsetsSourceProvider provider = target.getControllableInsetProvider();
- if (provider == null) {
- return mState;
- }
- final @InternalInsetsType int type = provider.getSource().getType();
- return getInsetsForType(type);
+ final @InternalInsetsType int type = provider != null
+ ? provider.getSource().getType() : ITYPE_INVALID;
+ return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode());
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type);
- if (type == ITYPE_INVALID) {
- return mState;
- }
- return getInsetsForType(type);
+ final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
+ final @WindowingMode int windowingMode = token != null
+ ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ return getInsetsForTypeAndWindowingMode(type, windowingMode);
}
- @InternalInsetsType
- private static int getInsetsTypeForWindowType(int type) {
+ private static @InternalInsetsType int getInsetsTypeForWindowType(int type) {
switch(type) {
case TYPE_STATUS_BAR:
return ITYPE_STATUS_BAR;
@@ -110,36 +111,48 @@
}
}
- private InsetsState getInsetsForType(@InternalInsetsType int type) {
- final InsetsState state = new InsetsState();
- state.set(mState);
- state.removeSource(type);
+ /** @see #getInsetsForDispatch */
+ private InsetsState getInsetsForTypeAndWindowingMode(@InternalInsetsType int type,
+ @WindowingMode int windowingMode) {
+ InsetsState state = mState;
- // Navigation bar doesn't get influenced by anything else
- if (type == ITYPE_NAVIGATION_BAR) {
- state.removeSource(ITYPE_IME);
- state.removeSource(ITYPE_STATUS_BAR);
- state.removeSource(ITYPE_CAPTION_BAR);
- }
+ if (type != ITYPE_INVALID) {
+ state = new InsetsState(state);
+ state.removeSource(type);
- // Status bar doesn't get influenced by caption bar
- if (type == ITYPE_STATUS_BAR) {
- state.removeSource(ITYPE_CAPTION_BAR);
- }
+ // Navigation bar doesn't get influenced by anything else
+ if (type == ITYPE_NAVIGATION_BAR) {
+ state.removeSource(ITYPE_IME);
+ state.removeSource(ITYPE_STATUS_BAR);
+ state.removeSource(ITYPE_CAPTION_BAR);
+ }
- // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
- if (type == ITYPE_IME) {
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- InsetsSourceProvider otherProvider = mProviders.valueAt(i);
- if (otherProvider.overridesImeFrame()) {
- InsetsSource override =
- new InsetsSource(state.getSource(otherProvider.getSource().getType()));
- override.setFrame(otherProvider.getImeOverrideFrame());
- state.addSource(override);
+ // Status bar doesn't get influenced by caption bar
+ if (type == ITYPE_STATUS_BAR) {
+ state.removeSource(ITYPE_CAPTION_BAR);
+ }
+
+ // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
+ if (type == ITYPE_IME) {
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ InsetsSourceProvider otherProvider = mProviders.valueAt(i);
+ if (otherProvider.overridesImeFrame()) {
+ InsetsSource override =
+ new InsetsSource(
+ state.getSource(otherProvider.getSource().getType()));
+ override.setFrame(otherProvider.getImeOverrideFrame());
+ state.addSource(override);
+ }
}
}
}
+ if (WindowConfiguration.isFloating(windowingMode)) {
+ state = new InsetsState(state);
+ state.removeSource(ITYPE_STATUS_BAR);
+ state.removeSource(ITYPE_NAVIGATION_BAR);
+ }
+
return state;
}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 660706e..9371c0e 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -16,7 +16,9 @@
package com.android.server.wm;
+import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
import android.os.Environment;
@@ -87,9 +89,17 @@
* launching activity of tasks) to {@link PersistableLaunchParams} that stores launch metadata
* that are stable across reboots.
*/
- private final SparseArray<ArrayMap<ComponentName, PersistableLaunchParams>> mMap =
+ private final SparseArray<ArrayMap<ComponentName, PersistableLaunchParams>> mLaunchParamsMap =
new SparseArray<>();
+ /**
+ * A map from {@link android.content.pm.ActivityInfo.WindowLayout#windowLayoutAffinity} to
+ * activity's component name for reverse queries from window layout affinities to activities.
+ * Used to decide if we should use another activity's record with the same affinity.
+ */
+ private final ArrayMap<String, ArraySet<ComponentName>> mWindowLayoutAffinityMap =
+ new ArrayMap<>();
+
LaunchParamsPersister(PersisterQueue persisterQueue, ActivityStackSupervisor supervisor) {
this(persisterQueue, supervisor, Environment::getDataSystemCeDirectory);
}
@@ -112,7 +122,7 @@
}
void onCleanupUser(int userId) {
- mMap.remove(userId);
+ mLaunchParamsMap.remove(userId);
}
private void loadLaunchParams(int userId) {
@@ -128,7 +138,7 @@
final File[] paramsFiles = launchParamsFolder.listFiles();
final ArrayMap<ComponentName, PersistableLaunchParams> map =
new ArrayMap<>(paramsFiles.length);
- mMap.put(userId, map);
+ mLaunchParamsMap.put(userId, map);
for (File paramsFile : paramsFiles) {
if (!paramsFile.isFile()) {
@@ -179,10 +189,12 @@
continue;
}
- params.restoreFromXml(parser);
+ params.restore(paramsFile, parser);
}
map.put(name, params);
+ addComponentNameToLaunchParamAffinityMapIfNotNull(
+ name, params.mWindowLayoutAffinity);
} catch (Exception e) {
Slog.w(TAG, "Failed to restore launch params for " + name, e);
filesToDelete.add(paramsFile);
@@ -204,19 +216,17 @@
final ComponentName name = task.realActivity;
final int userId = task.mUserId;
PersistableLaunchParams params;
- ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
+ ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId);
if (map == null) {
map = new ArrayMap<>();
- mMap.put(userId, map);
+ mLaunchParamsMap.put(userId, map);
}
- params = map.get(name);
- if (params == null) {
- params = new PersistableLaunchParams();
- map.put(name, params);
- }
+ params = map.computeIfAbsent(name, componentName -> new PersistableLaunchParams());
final boolean changed = saveTaskToLaunchParam(task, display, params);
+ addComponentNameToLaunchParamAffinityMapIfNotNull(name, params.mWindowLayoutAffinity);
+
if (changed) {
mPersisterQueue.updateLastOrAddItem(
new LaunchParamsWriteQueueItem(userId, name, params),
@@ -243,19 +253,63 @@
params.mBounds.setEmpty();
}
+ String launchParamAffinity = task.mWindowLayoutAffinity;
+ changed |= Objects.equals(launchParamAffinity, params.mWindowLayoutAffinity);
+ params.mWindowLayoutAffinity = launchParamAffinity;
+
+ if (changed) {
+ params.mTimestamp = System.currentTimeMillis();
+ }
+
return changed;
}
+ private void addComponentNameToLaunchParamAffinityMapIfNotNull(
+ ComponentName name, String launchParamAffinity) {
+ if (launchParamAffinity == null) {
+ return;
+ }
+ mWindowLayoutAffinityMap.computeIfAbsent(launchParamAffinity, affinity -> new ArraySet<>())
+ .add(name);
+ }
+
void getLaunchParams(Task task, ActivityRecord activity, LaunchParams outParams) {
final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
final int userId = task != null ? task.mUserId : activity.mUserId;
+ final String windowLayoutAffinity;
+ if (task != null) {
+ windowLayoutAffinity = task.mWindowLayoutAffinity;
+ } else {
+ ActivityInfo.WindowLayout layout = activity.info.windowLayout;
+ windowLayoutAffinity = layout == null ? null : layout.windowLayoutAffinity;
+ }
outParams.reset();
- Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
+ Map<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId);
if (map == null) {
return;
}
- final PersistableLaunchParams persistableParams = map.get(name);
+
+ // First use its own record as a reference.
+ PersistableLaunchParams persistableParams = map.get(name);
+ // Next we'll compare these params against all existing params with the same affinity and
+ // use the newest one.
+ if (windowLayoutAffinity != null
+ && mWindowLayoutAffinityMap.get(windowLayoutAffinity) != null) {
+ ArraySet<ComponentName> candidates = mWindowLayoutAffinityMap.get(windowLayoutAffinity);
+ for (int i = 0; i < candidates.size(); ++i) {
+ ComponentName candidate = candidates.valueAt(i);
+ final PersistableLaunchParams candidateParams = map.get(candidate);
+ if (candidateParams == null) {
+ continue;
+ }
+
+ if (persistableParams == null
+ || candidateParams.mTimestamp > persistableParams.mTimestamp) {
+ persistableParams = candidateParams;
+ }
+ }
+ }
if (persistableParams == null) {
return;
@@ -272,10 +326,10 @@
void removeRecordForPackage(String packageName) {
final List<File> fileToDelete = new ArrayList<>();
- for (int i = 0; i < mMap.size(); ++i) {
- int userId = mMap.keyAt(i);
+ for (int i = 0; i < mLaunchParamsMap.size(); ++i) {
+ int userId = mLaunchParamsMap.keyAt(i);
final File launchParamsFolder = getLaunchParamFolder(userId);
- ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.valueAt(i);
+ ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.valueAt(i);
for (int j = map.size() - 1; j >= 0; --j) {
final ComponentName name = map.keyAt(j);
if (name.getPackageName().equals(packageName)) {
@@ -409,6 +463,7 @@
private static final String ATTR_WINDOWING_MODE = "windowing_mode";
private static final String ATTR_DISPLAY_UNIQUE_ID = "display_unique_id";
private static final String ATTR_BOUNDS = "bounds";
+ private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
/** The bounds within the parent container. */
final Rect mBounds = new Rect();
@@ -419,14 +474,29 @@
/** The windowing mode to be in. */
int mWindowingMode;
+ /**
+ * Last {@link android.content.pm.ActivityInfo.WindowLayout#windowLayoutAffinity} of the
+ * window.
+ */
+ @Nullable String mWindowLayoutAffinity;
+
+ /**
+ * Timestamp from {@link System#currentTimeMillis()} when this record is captured, or last
+ * modified time when the record is restored from storage.
+ */
+ long mTimestamp;
+
void saveToXml(XmlSerializer serializer) throws IOException {
serializer.attribute(null, ATTR_DISPLAY_UNIQUE_ID, mDisplayUniqueId);
serializer.attribute(null, ATTR_WINDOWING_MODE,
Integer.toString(mWindowingMode));
serializer.attribute(null, ATTR_BOUNDS, mBounds.flattenToString());
+ if (mWindowLayoutAffinity != null) {
+ serializer.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
+ }
}
- void restoreFromXml(XmlPullParser parser) {
+ void restore(File xmlFile, XmlPullParser parser) {
for (int i = 0; i < parser.getAttributeCount(); ++i) {
final String attrValue = parser.getAttributeValue(i);
switch (parser.getAttributeName(i)) {
@@ -443,16 +513,28 @@
}
break;
}
+ case ATTR_WINDOW_LAYOUT_AFFINITY:
+ mWindowLayoutAffinity = attrValue;
+ break;
}
}
+
+ // The modified time could be a few seconds later than the timestamp when the record is
+ // captured, which is a good enough estimate to the capture time after a reboot or a
+ // user switch.
+ mTimestamp = xmlFile.lastModified();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder("PersistableLaunchParams{");
- builder.append("windowingMode=" + mWindowingMode);
+ builder.append(" windowingMode=" + mWindowingMode);
builder.append(" displayUniqueId=" + mDisplayUniqueId);
builder.append(" bounds=" + mBounds);
+ if (mWindowLayoutAffinity != null) {
+ builder.append(" launchParamsAffinity=" + mWindowLayoutAffinity);
+ }
+ builder.append(" timestamp=" + mTimestamp);
builder.append(" }");
return builder.toString();
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index fc358ce..bd5666d 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -846,10 +846,9 @@
@VisibleForTesting
Set<Integer> getProfileIds(int userId) {
Set<Integer> userIds = new ArraySet<>();
- final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId,
- false /* enabledOnly */);
- for (int i = profiles.size() - 1; i >= 0; --i) {
- userIds.add(profiles.get(i).id);
+ int[] profileIds = mService.getUserManager().getProfileIds(userId, false /* enabledOnly */);
+ for (int i = 0; i < profileIds.length; i++) {
+ userIds.add(Integer.valueOf(profileIds[i]));
}
return userIds;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 15a49a7..ebf1bc9 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2695,7 +2695,9 @@
return true;
}
} else {
- if (r.intent.getComponent().equals(cls)) {
+ // Compare the target component instead of intent component so we don't miss if the
+ // activity uses alias.
+ if (r.mActivityComponent.equals(cls)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 9df4248..5f3732a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -356,6 +356,31 @@
}
@Override
+ public void setWallpaperZoomOut(IBinder window, float zoom) {
+ if (Float.compare(0f, zoom) > 0 || Float.compare(1f, zoom) < 0 || Float.isNaN(zoom)) {
+ throw new IllegalArgumentException("Zoom must be a valid float between 0 and 1: "
+ + zoom);
+ }
+ synchronized (mService.mGlobalLock) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.setWallpaperZoomOut(windowState, zoom));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
+ public void setShouldZoomOutWallpaper(IBinder window, boolean shouldZoom) {
+ synchronized (mService.mGlobalLock) {
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.setShouldZoomOutWallpaper(windowState, shouldZoom));
+ }
+ }
+
+ @Override
public void wallpaperOffsetsComplete(IBinder window) {
synchronized (mService.mGlobalLock) {
actionOnWallpaper(window, (wpController, windowState) ->
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 19f8ca9..aa817fd 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -36,6 +36,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.function.Supplier;
/**
* A class that can run animations on objects that have a set of child surfaces. We do this by
@@ -145,7 +146,7 @@
if (mLeash == null) {
mLeash = createAnimationLeash(mAnimatable, surface, t, type,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
- 0 /* y */, hidden);
+ 0 /* y */, hidden, mService.mTransactionFactory);
mAnimatable.onAnimationLeashCreated(t, mLeash);
}
mAnimatable.onLeashAnimationStarting(t, mLeash);
@@ -374,13 +375,21 @@
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
Transaction t, @AnimationType int type, int width, int height, int x, int y,
- boolean hidden) {
+ boolean hidden, Supplier<Transaction> transactionFactory) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
.setParent(animatable.getAnimationLeashParent())
- .setHidden(hidden)
.setName(surface + " - animation-leash");
final SurfaceControl leash = builder.build();
+ if (!hidden) {
+ // TODO(b/151665759) Defer reparent calls
+ // We want the leash to be visible immediately but we want to set the effects on
+ // the layer. Since the transaction used in this function may be deferred, we apply
+ // another transaction immediately with the correct visibility and effects.
+ // If this doesn't work, you will can see the 2/3 button nav bar flicker during
+ // seamless rotation.
+ transactionFactory.get().unsetColor(leash).show(leash).apply();
+ }
t.setWindowCrop(leash, width, height);
t.setPosition(leash, x, y);
t.show(leash);
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 36861aa..8ab5043 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -75,7 +75,8 @@
mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
t, ANIMATION_TYPE_SCREEN_ROTATION, startBounds.width(), startBounds.height(),
- startBounds.left, startBounds.top, false /* hidden */);
+ startBounds.left, startBounds.top, false /* hidden */,
+ mWmService.mTransactionFactory);
mAnimatable.onAnimationLeashCreated(t, mLeash);
SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
@@ -103,7 +104,7 @@
*/
void unfreeze(SurfaceControl.Transaction t) {
if (mSnapshot != null) {
- mSnapshot.destroy(t);
+ mSnapshot.cancelAnimation(t, false /* restarting */);
}
if (mLeash == null) {
return;
@@ -212,7 +213,7 @@
* @param t The transaction to use for all cancelling surface operations.
* @param restarting Whether we are restarting the animation.
*/
- private void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
+ void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
final SurfaceControl leash = mSurfaceControl;
final AnimationAdapter animation = mAnimation;
final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
@@ -229,7 +230,6 @@
}
}
if (!restarting) {
- // TODO: do we need to destroy?
destroy(t);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b2db99b..f826deb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -199,6 +199,7 @@
private static final String ATTR_MIN_WIDTH = "min_width";
private static final String ATTR_MIN_HEIGHT = "min_height";
private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
+ private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
// Current version of the task record we persist. Used to check if we need to run any upgrade
// code.
@@ -233,6 +234,8 @@
String affinity; // The affinity name for this task, or null; may change identity.
String rootAffinity; // Initial base affinity, or null; does not change from initial root.
+ String mWindowLayoutAffinity; // Launch param affinity of this task or null. Used when saving
+ // launch params of this task.
IVoiceInteractionSession voiceSession; // Voice interaction session driving task
IVoiceInteractor voiceInteractor; // Associated interactor to provide to app
Intent intent; // The original intent that started the task. Note that this value can
@@ -428,7 +431,10 @@
private boolean mForceShowForAllUsers;
/** When set, will force the task to report as invisible. */
- boolean mForceHidden = false;
+ static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
+ static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
+ private int mForceHiddenFlags = 0;
+
SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
@@ -474,6 +480,7 @@
* taskAppeared callback, and emit a taskRemoved callback when the Task is vanished.
*/
ITaskOrganizer mTaskOrganizer;
+ private int mLastTaskOrganizerWindowingMode = -1;
/**
* Last Picture-in-Picture params applicable to the task. Updated when the app
@@ -1000,6 +1007,8 @@
origActivity = new ComponentName(info.packageName, info.name);
}
}
+ mWindowLayoutAffinity =
+ info.windowLayout == null ? null : info.windowLayout.windowLayoutAffinity;
final int intentFlags = intent == null ? 0 : intent.getFlags();
if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
@@ -1929,6 +1938,7 @@
// TODO: Should also take care of Pip mode changes here.
saveLaunchingStateIfNeeded();
+ updateTaskOrganizerState(false /* forceUpdate */);
}
/**
@@ -2120,14 +2130,26 @@
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
}
+ /**
+ * Forces the app bounds related configuration can be computed by
+ * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
+ * ActivityRecord.CompatDisplayInsets)}.
+ */
+ private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
+ final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (appBounds != null) {
+ appBounds.setEmpty();
+ }
+ inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+ inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+ }
+
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
if (overrideDisplayInfo != null) {
// Make sure the screen related configs can be computed by the provided display info.
- inOutConfig.windowConfiguration.setAppBounds(null);
inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
- inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
- inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+ invalidateAppBoundsConfig(inOutConfig);
}
computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
null /* compatInsets */);
@@ -2142,6 +2164,10 @@
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@NonNull Configuration parentConfig,
@Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ if (compatInsets != null) {
+ // Make sure the app bounds can be computed by the compat insets.
+ invalidateAppBoundsConfig(inOutConfig);
+ }
computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
compatInsets);
}
@@ -3024,7 +3050,7 @@
*/
@VisibleForTesting
boolean isTranslucent(ActivityRecord starting) {
- if (!isAttached() || mForceHidden) {
+ if (!isAttached() || isForceHidden()) {
return true;
}
final PooledPredicate p = PooledLambda.obtainPredicate(Task::isOpaqueActivity,
@@ -3363,6 +3389,9 @@
} else {
info.pictureInPictureParams = mPictureInPictureParams;
}
+ info.topActivityInfo = mReuseActivitiesReport.top != null
+ ? mReuseActivitiesReport.top.info
+ : null;
}
/**
@@ -3408,6 +3437,9 @@
pw.println();
}
}
+ if (mWindowLayoutAffinity != null) {
+ pw.print(prefix); pw.print("windowLayoutAffinity="); pw.println(mWindowLayoutAffinity);
+ }
if (voiceSession != null || voiceInteractor != null) {
pw.print(prefix); pw.print("VOICE: session=0x");
pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
@@ -3589,6 +3621,9 @@
} else if (rootAffinity != null) {
out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
}
+ if (mWindowLayoutAffinity != null) {
+ out.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
+ }
out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
@@ -3746,6 +3781,7 @@
String affinity = null;
String rootAffinity = null;
boolean hasRootAffinity = false;
+ String windowLayoutAffinity = null;
boolean rootHasReset = false;
boolean autoRemoveRecents = false;
boolean askedCompatMode = false;
@@ -3798,6 +3834,9 @@
rootAffinity = attrValue;
hasRootAffinity = true;
break;
+ case ATTR_WINDOW_LAYOUT_AFFINITY:
+ windowLayoutAffinity = attrValue;
+ break;
case ATTR_ROOTHASRESET:
rootHasReset = Boolean.parseBoolean(attrValue);
break;
@@ -3953,6 +3992,7 @@
realActivitySuspended, userSetupComplete, minWidth, minHeight, null /*stack*/);
task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
task.setBounds(lastNonFullscreenBounds);
+ task.mWindowLayoutAffinity = windowLayoutAffinity;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
task.addChild(activities.get(activityNdx));
@@ -4008,16 +4048,57 @@
return;
}
// Let the old organizer know it has lost control.
- if (mTaskOrganizer != null) {
- sendTaskVanished();
- }
+ sendTaskVanished();
mTaskOrganizer = organizer;
sendTaskAppeared();
+ onTaskOrganizerChanged();
}
// Called on Binder death.
void taskOrganizerDied() {
mTaskOrganizer = null;
+ mLastTaskOrganizerWindowingMode = -1;
+ onTaskOrganizerChanged();
+ }
+
+ /**
+ * Called when the task state changes (ie. from windowing mode change) an the task organizer
+ * state should also be updated.
+ *
+ * @param forceUpdate Updates the task organizer to the one currently specified in the task
+ * org controller for the task's windowing mode, ignoring the cached
+ * windowing mode checks.
+ */
+ void updateTaskOrganizerState(boolean forceUpdate) {
+ if (!isRootTask()) {
+ return;
+ }
+
+ final int windowingMode = getWindowingMode();
+ if (!forceUpdate && windowingMode == mLastTaskOrganizerWindowingMode) {
+ // If our windowing mode hasn't actually changed, then just stick
+ // with our old organizer. This lets us implement the semantic
+ // where SysUI can continue to manage it's old tasks
+ // while CTS temporarily takes over the registration.
+ return;
+ }
+ /*
+ * Different windowing modes may be managed by different task organizers. If
+ * getTaskOrganizer returns null, we still call setTaskOrganizer to
+ * make sure we clear it.
+ */
+ final ITaskOrganizer org =
+ mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
+ setTaskOrganizer(org);
+ mLastTaskOrganizerWindowingMode = windowingMode;
+ }
+
+ private void onTaskOrganizerChanged() {
+ if (mTaskOrganizer == null) {
+ // If this task is no longer controlled by a task organizer, then reset the force hidden
+ // state
+ setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */);
+ }
}
@Override
@@ -4123,8 +4204,41 @@
return mMainWindowSizeChangeTransaction;
}
+ void setActivityWindowingMode(int windowingMode) {
+ PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode,
+ PooledLambda.__(ActivityRecord.class), windowingMode);
+ forAllActivities(c);
+ c.recycle();
+ }
+
+ /**
+ * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
+ * @return Whether the force hidden state changed
+ */
+ boolean setForceHidden(int flags, boolean set) {
+ int newFlags = mForceHiddenFlags;
+ if (set) {
+ newFlags |= flags;
+ } else {
+ newFlags &= ~flags;
+ }
+ if (mForceHiddenFlags == newFlags) {
+ return false;
+ }
+ mForceHiddenFlags = newFlags;
+ return true;
+ }
+
+ /**
+ * Returns whether this task is currently forced to be hidden for any reason.
+ */
+ protected boolean isForceHidden() {
+ return mForceHiddenFlags != 0;
+ }
+
@Override
long getProtoFieldId() {
return TASK;
}
+
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4093fe5..8f09f3f 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,6 +23,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -105,7 +106,7 @@
TaskOrganizerState(ITaskOrganizer organizer, int windowingMode,
- TaskOrganizerState replacing) {
+ @Nullable TaskOrganizerState replacing) {
mOrganizer = organizer;
mDeathRecipient = new DeathRecipient(organizer, windowingMode);
try {
@@ -203,10 +204,27 @@
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
+ if (getTaskOrganizer(windowingMode) != null) {
+ Slog.w(TAG, "Task organizer already exists for windowing mode: "
+ + windowingMode);
+ }
+ final TaskOrganizerState previousState =
+ mTaskOrganizersForWindowingMode.get(windowingMode);
final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode,
- mTaskOrganizersForWindowingMode.get(windowingMode));
+ previousState);
mTaskOrganizersForWindowingMode.put(windowingMode, state);
mTaskOrganizerStates.put(organizer.asBinder(), state);
+
+ if (previousState == null) {
+ // Only in the case where this is the root task organizer for the given
+ // windowing mode, we add report all existing tasks in that mode to the new
+ // task organizer.
+ mService.mRootWindowContainer.forAllTasks((task) -> {
+ if (task.getWindowingMode() == windowingMode) {
+ task.updateTaskOrganizerState(true /* forceUpdate */);
+ }
+ });
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -458,6 +476,7 @@
if (!(container instanceof Task)) {
throw new RuntimeException("Invalid token in task transaction");
}
+ final Task task = (Task) container;
// The "client"-facing API should prevent bad changes; however, just in case, sanitize
// masks here.
int configMask = change.getConfigSetMask();
@@ -481,6 +500,11 @@
effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
}
+ if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+ if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
return effects;
}
@@ -553,18 +577,28 @@
WindowContainerTransaction.Change c) {
int effects = sanitizeAndApplyChange(wc, c);
+ final Task tr = wc.asTask();
+
final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
if (t != null) {
- Task tr = (Task) wc;
tr.setMainWindowSizeChangeTransaction(t);
}
Rect enterPipBounds = c.getEnterPipBounds();
if (enterPipBounds != null) {
- Task tr = (Task) wc;
mService.mStackSupervisor.updatePictureInPictureMode(tr,
enterPipBounds, true);
}
+
+ final int windowingMode = c.getWindowingMode();
+ if (windowingMode > -1) {
+ tr.setWindowingMode(windowingMode);
+ }
+ final int childWindowingMode = c.getActivityWindowingMode();
+ if (childWindowingMode > -1) {
+ tr.setActivityWindowingMode(childWindowingMode);
+ }
+
return effects;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f83b052..0f5cafe 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -352,9 +352,19 @@
}
task.getBounds(mTmpRect);
mTmpRect.offsetTo(0, 0);
+
+ SurfaceControl[] excludeLayers;
+ final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
+ if (imeWindow != null) {
+ excludeLayers = new SurfaceControl[1];
+ excludeLayers[0] = imeWindow.getSurfaceControl();
+ } else {
+ excludeLayers = new SurfaceControl[0];
+ }
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
- SurfaceControl.captureLayers(
- task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
+ SurfaceControl.captureLayersExcluding(
+ task.getSurfaceControl(), mTmpRect, scaleFraction,
+ pixelFormat, excludeLayers);
if (outTaskSize != null) {
outTaskSize.x = mTmpRect.width();
outTaskSize.y = mTmpRect.height();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 164d3e0..45023ac 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -99,10 +99,10 @@
if (lowResTaskSnapshotScale > 0) {
mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale;
- setEnableLowResSnapshots(true);
+ mEnableLowResSnapshots = true;
} else {
mLowResScaleFactor = 0;
- setEnableLowResSnapshots(false);
+ mEnableLowResSnapshots = false;
}
mUse16BitFormat = service.mContext.getResources().getBoolean(
@@ -175,14 +175,6 @@
}
/**
- * Not to be used. Only here for testing.
- */
- @VisibleForTesting
- void setEnableLowResSnapshots(boolean enabled) {
- mEnableLowResSnapshots = enabled;
- }
-
- /**
* Return if task snapshots are stored in 16 bit pixel format.
*
* @return true if task snapshots are stored in 16 bit pixel format.
@@ -405,7 +397,7 @@
return false;
}
- if (!enableLowResSnapshots()) {
+ if (!mEnableLowResSnapshots) {
swBitmap.recycle();
return true;
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 669eb78..57d0a33 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -41,6 +41,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArraySet;
+import android.util.MathUtils;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
@@ -52,6 +53,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Controls wallpaper windows visibility, ordering, and so on.
@@ -75,8 +77,10 @@
private float mLastWallpaperY = -1;
private float mLastWallpaperXStep = -1;
private float mLastWallpaperYStep = -1;
+ private float mLastWallpaperZoomOut = 0;
private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
+ private final float mMaxWallpaperScale;
// This is set when we are waiting for a wallpaper to tell us it is done
// changing its scroll position.
@@ -191,9 +195,21 @@
return false;
};
+ /**
+ * @see #computeLastWallpaperZoomOut()
+ */
+ private Consumer<WindowState> mComputeMaxZoomOutFunction = windowState -> {
+ if (!windowState.mIsWallpaper
+ && Float.compare(windowState.mWallpaperZoomOut, mLastWallpaperZoomOut) > 0) {
+ mLastWallpaperZoomOut = windowState.mWallpaperZoomOut;
+ }
+ };
+
WallpaperController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
+ mMaxWallpaperScale = service.mContext.getResources()
+ .getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale);
}
WindowState getWallpaperTarget() {
@@ -325,20 +341,30 @@
rawChanged = true;
}
- boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset);
+ if (Float.compare(wallpaperWin.mWallpaperZoomOut, mLastWallpaperZoomOut) != 0) {
+ wallpaperWin.mWallpaperZoomOut = mLastWallpaperZoomOut;
+ rawChanged = true;
+ }
+
+ boolean changed = wallpaperWin.mWinAnimator.setWallpaperOffset(xOffset, yOffset,
+ wallpaperWin.mShouldScaleWallpaper
+ ? zoomOutToScale(wallpaperWin.mWallpaperZoomOut) : 1);
if (rawChanged && (wallpaperWin.mAttrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) {
try {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset "
+ wallpaperWin + " x=" + wallpaperWin.mWallpaperX
- + " y=" + wallpaperWin.mWallpaperY);
+ + " y=" + wallpaperWin.mWallpaperY
+ + " zoom=" + wallpaperWin.mWallpaperZoomOut);
if (sync) {
mWaitingOnWallpaper = wallpaperWin;
}
wallpaperWin.mClient.dispatchWallpaperOffsets(
wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY,
- wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, sync);
+ wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep,
+ wallpaperWin.mWallpaperZoomOut, sync);
+
if (sync) {
if (mWaitingOnWallpaper != null) {
long start = SystemClock.uptimeMillis();
@@ -378,6 +404,20 @@
}
}
+ void setWallpaperZoomOut(WindowState window, float zoom) {
+ if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) {
+ window.mWallpaperZoomOut = zoom;
+ updateWallpaperOffsetLocked(window, false);
+ }
+ }
+
+ void setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom) {
+ if (shouldZoom != window.mShouldScaleWallpaper) {
+ window.mShouldScaleWallpaper = shouldZoom;
+ updateWallpaperOffsetLocked(window, false);
+ }
+ }
+
void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) {
if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) {
window.mWallpaperDisplayOffsetX = x;
@@ -420,6 +460,7 @@
} else if (changingTarget.mWallpaperY >= 0) {
mLastWallpaperY = changingTarget.mWallpaperY;
}
+ computeLastWallpaperZoomOut();
if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
} else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
@@ -593,6 +634,9 @@
mLastWallpaperX = mWallpaperTarget.mWallpaperX;
mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
}
+ if (mWallpaperTarget.mWallpaperZoomOut >= 0) {
+ mLastWallpaperZoomOut = mWallpaperTarget.mWallpaperZoomOut;
+ }
if (mWallpaperTarget.mWallpaperY >= 0) {
mLastWallpaperY = mWallpaperTarget.mWallpaperY;
mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
@@ -762,6 +806,23 @@
return mTmpTopWallpaper;
}
+ /**
+ * Each window can request a zoom, example:
+ * - User is in overview, zoomed out.
+ * - User also pulls down the shade.
+ *
+ * This means that we always have to choose the largest zoom out that we have, otherwise
+ * we'll have conflicts and break the "depth system" mental model.
+ */
+ private void computeLastWallpaperZoomOut() {
+ mLastWallpaperZoomOut = 0;
+ mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true);
+ }
+
+ private float zoomOutToScale(float zoom) {
+ return MathUtils.lerp(1, mMaxWallpaperScale, 1 - zoom);
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId());
pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index a60db94..0b11dd2 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -96,8 +96,8 @@
mAnimationFrameCallback = frameTimeNs -> {
synchronized (mService.mGlobalLock) {
mAnimationFrameCallbackScheduled = false;
+ animate(frameTimeNs);
}
- animate(frameTimeNs);
};
}
@@ -115,110 +115,94 @@
mInitialized = true;
}
- /**
- * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
- * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
- * sure other threads can make progress if this happens.
- */
private void animate(long frameTimeNs) {
-
- synchronized (mService.mGlobalLock) {
- if (!mInitialized) {
- return;
- }
-
- // Schedule next frame already such that back-pressure happens continuously
- scheduleAnimation();
+ if (!mInitialized) {
+ return;
}
- synchronized (mService.mGlobalLock) {
- mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
- mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
- if (DEBUG_WINDOW_TRACE) {
- Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
+ // Schedule next frame already such that back-pressure happens continuously.
+ scheduleAnimation();
+
+ mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
+ mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
+ }
+
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
+ mService.openSurfaceTransaction();
+ try {
+ final AccessibilityController accessibilityController =
+ mService.mAccessibilityController;
+ final int numDisplays = mDisplayContentsAnimators.size();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ // Update animations of all applications, including those associated with
+ // exiting/removed apps.
+ dc.updateWindowsForAnimator();
+ dc.prepareSurfaces();
}
- ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
- mService.openSurfaceTransaction();
- try {
- final AccessibilityController accessibilityController =
- mService.mAccessibilityController;
- final int numDisplays = mDisplayContentsAnimators.size();
- for (int i = 0; i < numDisplays; i++) {
- final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
- // Update animations of all applications, including those
- // associated with exiting/removed apps
- dc.updateWindowsForAnimator();
- dc.prepareSurfaces();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+
+ dc.checkAppWindowsReadyToShow();
+ if (accessibilityController != null) {
+ accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
+ mTransaction);
}
-
- for (int i = 0; i < numDisplays; i++) {
- final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-
- dc.checkAppWindowsReadyToShow();
- if (accessibilityController != null) {
- accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
- mTransaction);
- }
- }
-
- cancelAnimation();
-
- if (mService.mWatermark != null) {
- mService.mWatermark.drawIfNeeded();
- }
-
- SurfaceControl.mergeToGlobalTransaction(mTransaction);
- } catch (RuntimeException e) {
- Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
- } finally {
- mService.closeSurfaceTransaction("WindowAnimator");
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
}
- boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
- boolean doRequest = false;
- if (mBulkUpdateParams != 0) {
- doRequest = mService.mRoot.copyAnimToLayoutParams();
+ cancelAnimation();
+
+ if (mService.mWatermark != null) {
+ mService.mWatermark.drawIfNeeded();
}
- if (hasPendingLayoutChanges || doRequest) {
- mService.mWindowPlacerLocked.requestTraversal();
- }
+ SurfaceControl.mergeToGlobalTransaction(mTransaction);
+ } catch (RuntimeException e) {
+ Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
+ } finally {
+ mService.closeSurfaceTransaction("WindowAnimator");
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
+ }
- final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN);
- if (rootAnimating && !mLastRootAnimating) {
+ final boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
+ final boolean doRequest = mBulkUpdateParams != 0 && mService.mRoot.copyAnimToLayoutParams();
+ if (hasPendingLayoutChanges || doRequest) {
+ mService.mWindowPlacerLocked.requestTraversal();
+ }
- // Usually app transitions but quite a load onto the system already (with all the
- // things happening in app), so pause task snapshot persisting to not increase the
- // load.
- mService.mTaskSnapshotController.setPersisterPaused(true);
- Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
- }
- if (!rootAnimating && mLastRootAnimating) {
- mService.mWindowPlacerLocked.requestTraversal();
- mService.mTaskSnapshotController.setPersisterPaused(false);
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
- }
+ final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN);
+ if (rootAnimating && !mLastRootAnimating) {
+ // Usually app transitions but quite a load onto the system already (with all the things
+ // happening in app), so pause task snapshot persisting to not increase the load.
+ mService.mTaskSnapshotController.setPersisterPaused(true);
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
+ if (!rootAnimating && mLastRootAnimating) {
+ mService.mWindowPlacerLocked.requestTraversal();
+ mService.mTaskSnapshotController.setPersisterPaused(false);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
- mLastRootAnimating = rootAnimating;
+ mLastRootAnimating = rootAnimating;
- if (mRemoveReplacedWindows) {
- mService.mRoot.removeReplacedWindows();
- mRemoveReplacedWindows = false;
- }
+ if (mRemoveReplacedWindows) {
+ mService.mRoot.removeReplacedWindows();
+ mRemoveReplacedWindows = false;
+ }
- mService.destroyPreservedSurfaceLocked();
+ mService.destroyPreservedSurfaceLocked();
- executeAfterPrepareSurfacesRunnables();
+ executeAfterPrepareSurfacesRunnables();
- if (DEBUG_WINDOW_TRACE) {
- Slog.i(TAG, "!!! animate: exit"
- + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
- }
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.i(TAG, "!!! animate: exit"
+ + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e8897e1..a1902bb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -275,7 +275,7 @@
RemoteToken mRemoteToken = null;
BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
- SurfaceControl.Transaction mBLASTSyncTransaction = new SurfaceControl.Transaction();
+ SurfaceControl.Transaction mBLASTSyncTransaction;
boolean mUsingBLASTSyncTransaction = false;
BLASTSyncEngine.TransactionReadyListener mWaitingListener;
int mWaitingSyncId;
@@ -283,6 +283,7 @@
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
+ mBLASTSyncTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
mSurfaceFreezer = new SurfaceFreezer(this, wms);
}
@@ -2460,24 +2461,36 @@
mWaitingSyncId = -1;
}
- boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
- int waitingId) {
+ /**
+ * Returns true if any of the children elected to participate in the Sync
+ */
+ boolean addChildrenToSyncSet(int localId) {
boolean willSync = false;
- if (!isVisible()) {
- return willSync;
- }
- mUsingBLASTSyncTransaction = true;
- int localId = mBLASTSyncEngine.startSyncSet(this);
for (int i = 0; i < mChildren.size(); i++) {
final WindowContainer child = mChildren.get(i);
- willSync = mBLASTSyncEngine.addToSyncSet(localId, child) | willSync;
+ willSync |= mBLASTSyncEngine.addToSyncSet(localId, child);
}
+ return willSync;
+ }
+
+ boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
+ int waitingId) {
+ boolean willSync = true;
+
+ // If we are invisible, no need to sync, likewise if we are already engaged in a sync,
+ // we can't support overlapping syncs on a single container yet.
+ if (!isVisible() || mWaitingListener != null) {
+ return false;
+ }
+ mUsingBLASTSyncTransaction = true;
// Make sure to set these before we call setReady in case the sync was a no-op
mWaitingSyncId = waitingId;
mWaitingListener = waitingListener;
+ int localId = mBLASTSyncEngine.startSyncSet(this);
+ willSync |= addChildrenToSyncSet(localId);
mBLASTSyncEngine.setReady(localId);
return willSync;
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index 90e3be7..a27a112 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -97,7 +97,7 @@
// TODO: This should be attached as a child to the app token, once the thumbnail animations
// use relative coordinates. Once we start animating task we can also consider attaching
// this to the task.
- mSurfaceControl = mWindowContainer.makeSurface()
+ mSurfaceControl = mWindowContainer.makeChildSurface(mWindowContainer.getTopChild())
.setName("thumbnail anim: " + mWindowContainer.toString())
.setBufferSize(mWidth, mHeight)
.setFormat(PixelFormat.TRANSLUCENT)
@@ -209,7 +209,7 @@
@Override
public Builder makeAnimationLeash() {
- return mWindowContainer.makeSurface();
+ return mWindowContainer.makeChildSurface(mWindowContainer.getTopChild());
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ecbbb03..eb18678 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -186,7 +186,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -882,7 +881,13 @@
FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt(
resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
- mAtmService.mSupportsFreeformWindowManagement = freeformWindowManagement;
+ if (mAtmService.mSupportsFreeformWindowManagement != freeformWindowManagement) {
+ mAtmService.mSupportsFreeformWindowManagement = freeformWindowManagement;
+ synchronized (mGlobalLock) {
+ // Notify the root window container that the display settings value may change.
+ mRoot.onSettingsRetrieved();
+ }
+ }
}
void updateForceResizableTasks() {
@@ -1007,9 +1012,7 @@
void openSurfaceTransaction() {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
- synchronized (mGlobalLock) {
- SurfaceControl.openTransaction();
- }
+ SurfaceControl.openTransaction();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -1022,10 +1025,8 @@
void closeSurfaceTransaction(String where) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
- synchronized (mGlobalLock) {
- SurfaceControl.closeTransaction();
- mWindowTracing.logState(where);
- }
+ SurfaceControl.closeTransaction();
+ mWindowTracing.logState(where);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -1166,9 +1167,7 @@
mAnimator = new WindowAnimator(this);
mRoot = new RootWindowContainer(this);
- mUseBLAST = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
- WM_USE_BLAST_ADAPTER_FLAG, false);
+ mUseBLAST = true;
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mTaskSnapshotController = new TaskSnapshotController(this);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 32eb932..f356329 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1058,7 +1058,7 @@
}
private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
- if (activityRecord == null) {
+ if (activityRecord == null || activityRecord.containsListener(this)) {
return;
}
// A process can only register to one activityRecord to listen to the override configuration
@@ -1093,7 +1093,7 @@
for (int i = mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord activityRecord = mActivities.get(i);
- if (!activityRecord.finishing && !activityRecord.containsListener(this)) {
+ if (!activityRecord.finishing) {
// Eligible activity is found, update listener.
registerActivityConfigurationListener(activityRecord);
return;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fc28cd5..3617570 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -415,6 +415,7 @@
float mHScale=1, mVScale=1;
float mLastHScale=1, mLastVScale=1;
final Matrix mTmpMatrix = new Matrix();
+ final float[] mTmpMatrixArray = new float[9];
private final WindowFrames mWindowFrames = new WindowFrames();
@@ -446,6 +447,14 @@
float mWallpaperX = -1;
float mWallpaperY = -1;
+ // If a window showing a wallpaper: the requested zoom out for the
+ // wallpaper; if a wallpaper window: the currently applied zoom.
+ float mWallpaperZoomOut = -1;
+
+ // If a wallpaper window: whether the wallpaper should be scaled when zoomed, if set
+ // to false, mWallpaperZoom will be ignored here and just passed to the WallpaperService.
+ boolean mShouldScaleWallpaper;
+
// If a window showing a wallpaper: what fraction of the offset
// range corresponds to a full virtual screen.
float mWallpaperXStep = -1;
@@ -670,6 +679,15 @@
*/
int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
+ /**
+ * BLASTSyncEngine ID corresponding to a sync-set for all
+ * our children. We add our children to this set in Sync,
+ * but we save it and don't mark it as ready until finishDrawing
+ * this way we have a two way latch between all our children finishing
+ * and drawing ourselves.
+ */
+ private int mLocalSyncId = -1;
+
static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
/**
@@ -2920,8 +2938,9 @@
// and add the window only if the permission was granted. Therefore, if
// the mode is MODE_DEFAULT we want the op to succeed as the window is
// shown.
- final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp,
- getOwningUid(), getOwningPackage(), true);
+ final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, getOwningUid(),
+ getOwningPackage(), true /* startIfModeDefault */, null /* featureId */,
+ "init-default-visibility");
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
setAppOpVisibilityLw(false);
}
@@ -2929,7 +2948,8 @@
void resetAppOpsState() {
if (mAppOp != OP_NONE && mAppOpVisibility) {
- mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+ mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage(),
+ null /* featureId */);
}
}
@@ -2944,11 +2964,12 @@
// as this would mean we will get another change callback and will reconcile.
int mode = mWmService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
- mWmService.mAppOps.finishOp(mAppOp, uid, packageName);
+ mWmService.mAppOps.finishOp(mAppOp, uid, packageName, null /* featureId */);
setAppOpVisibilityLw(false);
}
} else {
- final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
+ final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName,
+ true /* startIfModeDefault */, null /* featureId */, "attempt-to-be-visible");
if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
setAppOpVisibilityLw(true);
}
@@ -3923,6 +3944,9 @@
pw.println(prefix + "mWallpaperXStep=" + mWallpaperXStep
+ " mWallpaperYStep=" + mWallpaperYStep);
}
+ if (mWallpaperZoomOut != -1) {
+ pw.println(prefix + "mWallpaperZoomOut=" + mWallpaperZoomOut);
+ }
if (mWallpaperDisplayOffsetX != Integer.MIN_VALUE
|| mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
pw.println(prefix + "mWallpaperDisplayOffsetX=" + mWallpaperDisplayOffsetX
@@ -5672,11 +5696,16 @@
@Override
boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
int waitingId) {
- // TODO(b/148871522): Support child window
+ if (!isVisible()) {
+ return false;
+ }
mWaitingListener = waitingListener;
mWaitingSyncId = waitingId;
mUsingBLASTSyncTransaction = true;
+ mLocalSyncId = mBLASTSyncEngine.startSyncSet(this);
+ addChildrenToSyncSet(mLocalSyncId);
+
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
BLAST_TIMEOUT_DURATION);
@@ -5690,11 +5719,21 @@
}
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
- if (postDrawTransaction == null) {
- postDrawTransaction = new SurfaceControl.Transaction();
+ if (postDrawTransaction != null) {
+ mBLASTSyncTransaction.merge(postDrawTransaction);
}
- postDrawTransaction.merge(mBLASTSyncTransaction);
- mWaitingListener.transactionReady(mWaitingSyncId, postDrawTransaction);
+
+ // If localSyncId is >0 then we are syncing with children and will
+ // invoke transaction ready from our own #transactionReady callback
+ // we just need to signal our side of the sync (setReady). But if we
+ // have no sync operation at this level transactionReady will never
+ // be invoked and we need to invoke it ourself.
+ if (mLocalSyncId >= 0) {
+ mBLASTSyncEngine.setReady(mLocalSyncId);
+ } else {
+ mWaitingListener.transactionReady(mWaitingSyncId, mBLASTSyncTransaction);
+ }
+
mUsingBLASTSyncTransaction = false;
mWaitingSyncId = 0;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 79dc536..563710b 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,6 +16,12 @@
package com.android.server.wm;
+import static android.graphics.Matrix.MSCALE_X;
+import static android.graphics.Matrix.MSCALE_Y;
+import static android.graphics.Matrix.MSKEW_X;
+import static android.graphics.Matrix.MSKEW_Y;
+import static android.graphics.Matrix.MTRANS_X;
+import static android.graphics.Matrix.MTRANS_Y;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -217,6 +223,10 @@
int mXOffset = 0;
int mYOffset = 0;
+ // A scale factor for the surface contents, that will be applied from the center of the visible
+ // region.
+ float mWallpaperScale = 1f;
+
/**
* A flag to determine if the WSA needs to offset its position to compensate for the stack's
* position update before the WSA surface has resized.
@@ -1034,7 +1044,12 @@
}
}
}
- mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory);
+ if (!mIsWallpaper) {
+ mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory);
+ } else {
+ setWallpaperPositionAndScale(
+ xOffset, yOffset, mWallpaperScale, recoveringMemory);
+ }
}
}
@@ -1051,11 +1066,15 @@
if (!w.mSeamlesslyRotated) {
- applyCrop(clipRect, recoveringMemory);
- mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
- mDtDx * w.mVScale * mExtraVScale,
- mDtDy * w.mHScale * mExtraHScale,
- mDsDy * w.mVScale * mExtraVScale, recoveringMemory);
+ if (!mIsWallpaper) {
+ applyCrop(clipRect, recoveringMemory);
+ mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
+ mDtDx * w.mVScale * mExtraVScale,
+ mDtDy * w.mHScale * mExtraHScale,
+ mDsDy * w.mVScale * mExtraVScale, recoveringMemory);
+ } else {
+ setWallpaperPositionAndScale(mXOffset, mYOffset, mWallpaperScale, recoveringMemory);
+ }
}
if (mSurfaceResized) {
@@ -1202,18 +1221,18 @@
mSurfaceController.setTransparentRegionHint(region);
}
- boolean setWallpaperOffset(int dx, int dy) {
- if (mXOffset == dx && mYOffset == dy) {
+ boolean setWallpaperOffset(int dx, int dy, float scale) {
+ if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) {
return false;
}
mXOffset = dx;
mYOffset = dy;
+ mWallpaperScale = scale;
try {
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
mService.openSurfaceTransaction();
- mSurfaceController.setPositionInTransaction(dx, dy, false);
- applyCrop(null, false);
+ setWallpaperPositionAndScale(dx, dy, scale, false);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + mWin
+ " pos=(" + dx + "," + dy + ")", e);
@@ -1225,6 +1244,27 @@
}
}
+ private void setWallpaperPositionAndScale(int dx, int dy, float scale,
+ boolean recoveringMemory) {
+ DisplayInfo displayInfo = mWin.getDisplayInfo();
+ Matrix matrix = mWin.mTmpMatrix;
+ matrix.setTranslate(dx, dy);
+ matrix.postScale(scale, scale, displayInfo.logicalWidth / 2f,
+ displayInfo.logicalHeight / 2f);
+ matrix.getValues(mWin.mTmpMatrixArray);
+ matrix.reset();
+
+ mSurfaceController.setPositionInTransaction(mWin.mTmpMatrixArray[MTRANS_X],
+ mWin.mTmpMatrixArray[MTRANS_Y], recoveringMemory);
+ mSurfaceController.setMatrixInTransaction(
+ mDsDx * mWin.mTmpMatrixArray[MSCALE_X] * mWin.mHScale * mExtraHScale,
+ mDtDx * mWin.mTmpMatrixArray[MSKEW_Y] * mWin.mVScale * mExtraVScale,
+ mDtDy * mWin.mTmpMatrixArray[MSKEW_X] * mWin.mHScale * mExtraHScale,
+ mDsDy * mWin.mTmpMatrixArray[MSCALE_Y] * mWin.mVScale * mExtraVScale,
+ recoveringMemory);
+ applyCrop(null, recoveringMemory);
+ }
+
/**
* Try to change the pixel format without recreating the surface. This
* will be common in the case of changing from PixelFormat.OPAQUE to
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 336934e..822f383 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1448,13 +1448,13 @@
SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz);
- if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_RECEIVER_ISB) {
- SET(ReceiverInterSignalBiasNanos, measurement_V2_1->receiverInterSignalBiasNs);
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_FULL_ISB) {
+ SET(FullInterSignalBiasNanos, measurement_V2_1->fullInterSignalBiasNs);
}
- if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_RECEIVER_ISB_UNCERTAINTY) {
- SET(ReceiverInterSignalBiasUncertaintyNanos,
- measurement_V2_1->receiverInterSignalBiasUncertaintyNs);
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_FULL_ISB_UNCERTAINTY) {
+ SET(FullInterSignalBiasUncertaintyNanos,
+ measurement_V2_1->fullInterSignalBiasUncertaintyNs);
}
if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB) {
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 6f9d012..29b2cd6 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -172,26 +172,25 @@
BlockHeader readHeader(std::span<uint8_t>& data);
-static inline int32_t readBEInt32(borrowed_fd fd) {
+static inline int32_t readLEInt32(borrowed_fd fd) {
int32_t result;
ReadFully(fd, &result, sizeof(result));
- result = int32_t(be32toh(result));
+ result = int32_t(le32toh(result));
return result;
}
static inline std::vector<char> readBytes(borrowed_fd fd) {
- int32_t size = readBEInt32(fd);
+ int32_t size = readLEInt32(fd);
std::vector<char> result(size);
ReadFully(fd, result.data(), size);
return result;
}
static inline int32_t skipIdSigHeaders(borrowed_fd fd) {
- readBEInt32(fd); // version
- readBytes(fd); // verityRootHash
- readBytes(fd); // v3Digest
- readBytes(fd); // pkcs7SignatureBlock
- return readBEInt32(fd); // size of the verity tree
+ readLEInt32(fd); // version
+ readBytes(fd); // hashingInfo
+ readBytes(fd); // signingInfo
+ return readLEInt32(fd); // size of the verity tree
}
static inline IncFsSize verityTreeSizeForFile(IncFsSize fileSize) {
@@ -365,13 +364,8 @@
}
}
void onDestroy() final {
- ALOGE("Sending EXIT to server.");
- sendRequest(mOutFd, EXIT);
// Make sure the receiver thread stopped.
CHECK(!mReceiverThread.joinable());
-
- mInFd.reset();
- mOutFd.reset();
}
// Installation.
@@ -569,13 +563,6 @@
// Streaming.
bool initStreaming(unique_fd inout) {
- mInFd.reset(dup(inout));
- mOutFd.reset(dup(inout));
- if (mInFd < 0 || mOutFd < 0) {
- ALOGE("Failed to dup FDs.");
- return false;
- }
-
mEventFd.reset(eventfd(0, EFD_CLOEXEC));
if (mEventFd < 0) {
ALOGE("Failed to create eventfd.");
@@ -584,7 +571,7 @@
// Awaiting adb handshake.
char okay_buf[OKAY.size()];
- if (!android::base::ReadFully(mInFd, okay_buf, OKAY.size())) {
+ if (!android::base::ReadFully(inout, okay_buf, OKAY.size())) {
ALOGE("Failed to receive OKAY. Abort.");
return false;
}
@@ -594,13 +581,23 @@
return false;
}
- mReceiverThread = std::thread([this]() { receiver(); });
+ {
+ std::lock_guard lock{mOutFdLock};
+ mOutFd.reset(::dup(inout));
+ if (mOutFd < 0) {
+ ALOGE("Failed to create streaming fd.");
+ }
+ }
+
+ mReceiverThread =
+ std::thread([this, io = std::move(inout)]() mutable { receiver(std::move(io)); });
ALOGI("Started streaming...");
return true;
}
// IFS callbacks.
void onPendingReads(dataloader::PendingReads pendingReads) final {
+ std::lock_guard lock{mOutFdLock};
CHECK(mIfs);
for (auto&& pendingRead : pendingReads) {
const android::dataloader::FileId& fileId = pendingRead.id;
@@ -626,12 +623,12 @@
}
}
- void receiver() {
+ void receiver(unique_fd inout) {
std::vector<uint8_t> data;
std::vector<IncFsDataBlock> instructions;
std::unordered_map<FileIdx, unique_fd> writeFds;
while (!mStopReceiving) {
- const int res = waitForDataOrSignal(mInFd, mEventFd);
+ const int res = waitForDataOrSignal(inout, mEventFd);
if (res == 0) {
continue;
}
@@ -641,10 +638,11 @@
break;
}
if (res == mEventFd) {
- ALOGE("Received stop signal. Exit.");
+ ALOGE("Received stop signal. Sending EXIT to server.");
+ sendRequest(inout, EXIT);
break;
}
- if (!readChunk(mInFd, data)) {
+ if (!readChunk(inout, data)) {
ALOGE("Failed to read a message. Abort.");
mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
break;
@@ -657,7 +655,7 @@
ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).",
int(remainingData.size()));
- sendRequest(mOutFd, EXIT);
+ sendRequest(inout, EXIT);
mStopReceiving = true;
break;
}
@@ -700,6 +698,11 @@
writeInstructions(instructions);
}
writeInstructions(instructions);
+
+ {
+ std::lock_guard lock{mOutFdLock};
+ mOutFd.reset();
+ }
}
void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
@@ -743,7 +746,7 @@
std::string mArgs;
android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
android::dataloader::StatusListenerPtr mStatusListener = nullptr;
- android::base::unique_fd mInFd;
+ std::mutex mOutFdLock;
android::base::unique_fd mOutFd;
android::base::unique_fd mEventFd;
std::thread mReceiverThread;
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 239a101..fe05d4e 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -178,6 +178,92 @@
return isOk;
}
+enum class HalSupport {
+ UNKNOWN = 0,
+ ON,
+ OFF,
+};
+
+static void setPowerBoostWithHandle(sp<IPowerAidl> handle, Boost boost, int32_t durationMs) {
+ // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
+ // Need to increase the array size if more boost supported.
+ static std::array<std::atomic<HalSupport>,
+ static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+ boostSupportedArray = {HalSupport::UNKNOWN};
+
+ // Quick return if boost is not supported by HAL
+ if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
+ boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
+ ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str());
+ return;
+ }
+
+ if (boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ handle->isBoostSupported(boost, &isSupported);
+ boostSupportedArray[static_cast<int32_t>(boost)] =
+ isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setPowerBoost %s because HAL doesn't support it",
+ toString(boost).c_str());
+ return;
+ }
+ }
+
+ auto ret = handle->setBoost(boost, durationMs);
+ processPowerHalReturn(ret.isOk(), "setPowerBoost");
+}
+
+static void setPowerBoost(Boost boost, int32_t durationMs) {
+ std::unique_lock<std::mutex> lock(gPowerHalMutex);
+ if (connectPowerHalLocked() != HalVersion::AIDL) {
+ ALOGV("Power HAL AIDL not available");
+ return;
+ }
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerBoostWithHandle(handle, boost, durationMs);
+}
+
+static void setPowerModeWithHandle(sp<IPowerAidl> handle, Mode mode, bool enabled) {
+ // Android framework only sends mode upto DISPLAY_INACTIVE.
+ // Need to increase the array if more mode supported.
+ static std::array<std::atomic<HalSupport>, static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1>
+ modeSupportedArray = {HalSupport::UNKNOWN};
+
+ // Quick return if mode is not supported by HAL
+ if (mode > Mode::DISPLAY_INACTIVE ||
+ modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
+ ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
+ return;
+ }
+
+ if (modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
+ bool isSupported = false;
+ handle->isModeSupported(mode, &isSupported);
+ modeSupportedArray[static_cast<int32_t>(mode)] =
+ isSupported ? HalSupport::ON : HalSupport::OFF;
+ if (!isSupported) {
+ ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
+ return;
+ }
+ }
+
+ auto ret = handle->setMode(mode, enabled);
+ processPowerHalReturn(ret.isOk(), "setPowerMode");
+}
+
+static void setPowerMode(Mode mode, bool enabled) {
+ std::unique_lock<std::mutex> lock(gPowerHalMutex);
+ if (connectPowerHalLocked() != HalVersion::AIDL) {
+ ALOGV("Power HAL AIDL not available");
+ return;
+ }
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, mode, enabled);
+}
+
static void sendPowerHint(PowerHint hintId, uint32_t data) {
std::unique_lock<std::mutex> lock(gPowerHalMutex);
switch (connectPowerHalLocked()) {
@@ -201,14 +287,28 @@
if (hintId == PowerHint::INTERACTION) {
sp<IPowerAidl> handle = gPowerHalAidl_;
lock.unlock();
- auto ret = handle->setBoost(Boost::INTERACTION, data);
- processPowerHalReturn(ret.isOk(), "setBoost");
+ setPowerBoostWithHandle(handle, Boost::INTERACTION, data);
break;
} else if (hintId == PowerHint::LAUNCH) {
sp<IPowerAidl> handle = gPowerHalAidl_;
lock.unlock();
- auto ret = handle->setMode(Mode::LAUNCH, static_cast<bool>(data));
- processPowerHalReturn(ret.isOk(), "setMode");
+ setPowerModeWithHandle(handle, Mode::LAUNCH, static_cast<bool>(data));
+ break;
+ } else if (hintId == PowerHint::LOW_POWER) {
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::LOW_POWER, static_cast<bool>(data));
+ break;
+ } else if (hintId == PowerHint::SUSTAINED_PERFORMANCE) {
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::SUSTAINED_PERFORMANCE,
+ static_cast<bool>(data));
+ break;
+ } else if (hintId == PowerHint::VR_MODE) {
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::VR, static_cast<bool>(data));
break;
} else {
ALOGE("Unsupported power hint: %s.", toString(hintId).c_str());
@@ -223,87 +323,6 @@
SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId));
}
-enum class HalSupport {
- UNKNOWN = 0,
- ON,
- OFF,
-};
-
-static void setPowerBoost(Boost boost, int32_t durationMs) {
- // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
- // Need to increase the array size if more boost supported.
- static std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT) + 1>
- boostSupportedArray = {HalSupport::UNKNOWN};
-
- // Quick return if boost is not supported by HAL
- if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
- boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
- ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str());
- return;
- }
-
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- if (connectPowerHalLocked() != HalVersion::AIDL) {
- ALOGV("Power HAL AIDL not available");
- return;
- }
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
-
- if (boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
- bool isSupported = false;
- handle->isBoostSupported(boost, &isSupported);
- boostSupportedArray[static_cast<int32_t>(boost)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
- if (!isSupported) {
- ALOGV("Skipped setPowerBoost %s because HAL doesn't support it",
- toString(boost).c_str());
- return;
- }
- }
-
- auto ret = handle->setBoost(boost, durationMs);
- processPowerHalReturn(ret.isOk(), "setPowerBoost");
-}
-
-static void setPowerMode(Mode mode, bool enabled) {
- // Android framework only sends mode upto DISPLAY_INACTIVE.
- // Need to increase the array if more mode supported.
- static std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1>
- modeSupportedArray = {HalSupport::UNKNOWN};
-
- // Quick return if mode is not supported by HAL
- if (mode > Mode::DISPLAY_INACTIVE ||
- modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
- ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
- return;
- }
-
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- if (connectPowerHalLocked() != HalVersion::AIDL) {
- ALOGV("Power HAL AIDL not available");
- return;
- }
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
-
- if (modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
- bool isSupported = false;
- handle->isModeSupported(mode, &isSupported);
- modeSupportedArray[static_cast<int32_t>(mode)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
- if (!isSupported) {
- ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
- return;
- }
- }
-
- auto ret = handle->setMode(mode, enabled);
- processPowerHalReturn(ret.isOk(), "setPowerMode");
-}
-
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
if (gPowerManagerServiceObj) {
// Throttle calls into user activity by event type.
@@ -426,8 +445,7 @@
case HalVersion::AIDL: {
sp<IPowerAidl> handle = gPowerHalAidl_;
lock.unlock();
- auto ret = handle->setMode(Mode::INTERACTIVE, enable);
- processPowerHalReturn(ret.isOk(), "setMode");
+ setPowerModeWithHandle(handle, Mode::INTERACTIVE, enable);
return;
}
default: {
@@ -481,8 +499,9 @@
return;
}
case HalVersion::AIDL: {
- auto ret = gPowerHalAidl_->setMode(Mode::DOUBLE_TAP_TO_WAKE, static_cast<bool>(data));
- processPowerHalReturn(ret.isOk(), "setMode");
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::DOUBLE_TAP_TO_WAKE, static_cast<bool>(data));
return;
}
default: {
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index be11b86..0277f16 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -25,17 +25,14 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/fsverity.h>
+#include <linux/stat.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <type_traits>
-
#include <android-base/unique_fd.h>
-const int kSha256Bytes = 32;
-
namespace android {
namespace {
@@ -69,30 +66,28 @@
return 0;
}
-int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
- using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kSha256Bytes>;
-
- Storage bytes;
- fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
- data->digest_size = kSha256Bytes; // the only input/output parameter
-
+// Returns whether the file has fs-verity enabled.
+// 0 if it is not present, 1 if is present, and -errno if there was an error.
+int statxForFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath) {
ScopedUtfChars path(env, filePath);
- if (path.c_str() == nullptr) {
- return EINVAL;
+
+ struct statx out = {};
+ if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
+ return -errno;
}
- ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
- if (rfd.get() < 0) {
- return errno;
+
+ // Sanity check.
+ if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) {
+ ALOGE("Unexpected, STATX_ATTR_VERITY not supported by kernel");
+ return -ENOSYS;
}
- if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
- return errno;
- }
- return 0;
+
+ return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
}
const JNINativeMethod sMethods[] = {
- { "enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity },
- { "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
+ {"enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity},
+ {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity},
};
} // namespace
diff --git a/services/core/jni/com_android_server_tv_GamepadKeys.h b/services/core/jni/com_android_server_tv_GamepadKeys.h
new file mode 100644
index 0000000..11fc903
--- /dev/null
+++ b/services/core/jni/com_android_server_tv_GamepadKeys.h
@@ -0,0 +1,79 @@
+#ifndef ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_
+#define ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_
+
+#include <linux/input.h>
+
+namespace android {
+
+// Follows the W3 spec for gamepad buttons and their corresponding mapping into
+// Linux keycodes. Note that gamepads are generally not very well standardized
+// and various controllers will result in different buttons. This mapping tries
+// to be reasonable.
+//
+// W3 Button spec: https://www.w3.org/TR/gamepad/#remapping
+//
+// Standard gamepad keycodes are added plus 2 additional buttons (e.g. Stadia
+// has "Assistant" and "Share", PS4 has the touchpad button).
+//
+// To generate this list, PS4, XBox, Stadia and Nintendo Switch Pro were tested.
+static const int GAMEPAD_KEY_CODES[19] = {
+ // Right-side buttons. A/B/X/Y or circle/triangle/square/X or similar
+ BTN_A, // "South", A, GAMEPAD and SOUTH have the same constant
+ BTN_B, // "East", BTN_B, BTN_EAST have the same constant
+ BTN_X, // "West", Note that this maps to X and NORTH in constants
+ BTN_Y, // "North", Note that this maps to Y and WEST in constants
+
+ BTN_TL, // "Left Bumper" / "L1" - Nintendo sends BTN_WEST instead
+ BTN_TR, // "Right Bumper" / "R1" - Nintendo sends BTN_Z instead
+
+ // For triggers, gamepads vary:
+ // - Stadia sends analog values over ABS_GAS/ABS_BRAKE and sends
+ // TriggerHappy3/4 as digital presses
+ // - PS4 and Xbox send analog values as ABS_Z/ABS_RZ
+ // - Nintendo Pro sends BTN_TL/BTN_TR (since bumpers behave differently)
+ // As placeholders we chose the stadia trigger-happy values since TL/TR are
+ // sent for bumper button presses
+ BTN_TRIGGER_HAPPY4, // "Left Trigger" / "L2"
+ BTN_TRIGGER_HAPPY3, // "Right Trigger" / "R2"
+
+ BTN_SELECT, // "Select/Back". Often "options" or similar
+ BTN_START, // "Start/forward". Often "hamburger" icon
+
+ BTN_THUMBL, // "Left Joystick Pressed"
+ BTN_THUMBR, // "Right Joystick Pressed"
+
+ // For DPads, gamepads generally only send axis changes
+ // on ABS_HAT0X and ABS_HAT0Y.
+ KEY_UP, // "Digital Pad up"
+ KEY_DOWN, // "Digital Pad down"
+ KEY_LEFT, // "Digital Pad left"
+ KEY_RIGHT, // "Digital Pad right"
+
+ BTN_MODE, // "Main button" (Stadia/PS/XBOX/Home)
+
+ BTN_TRIGGER_HAPPY1, // Extra button: "Assistant" for Stadia
+ BTN_TRIGGER_HAPPY2, // Extra button: "Share" for Stadia
+};
+
+// Defines information for an axis.
+struct Axis {
+ int number;
+ int rangeMin;
+ int rangeMax;
+};
+
+// List of all axes supported by a gamepad
+static const Axis GAMEPAD_AXES[] = {
+ {ABS_X, 0, 254}, // Left joystick X
+ {ABS_Y, 0, 254}, // Left joystick Y
+ {ABS_RX, 0, 254}, // Right joystick X
+ {ABS_RY, 0, 254}, // Right joystick Y
+ {ABS_Z, 0, 254}, // Left trigger
+ {ABS_RZ, 0, 254}, // Right trigger
+ {ABS_HAT0X, -1, 1}, // DPad X
+ {ABS_HAT0Y, -1, 1}, // DPad Y
+};
+
+} // namespace android
+
+#endif // ANDROIDTVREMOTE_SERVICE_JNI_GAMEPAD_KEYS_H_
diff --git a/services/core/jni/com_android_server_tv_TvKeys.h b/services/core/jni/com_android_server_tv_TvKeys.h
index 4895f34..7eacdf6 100644
--- a/services/core/jni/com_android_server_tv_TvKeys.h
+++ b/services/core/jni/com_android_server_tv_TvKeys.h
@@ -110,4 +110,4 @@
} // namespace android
-#endif // SERVICE_JNI_KEYS_H_
+#endif // ANDROIDTVREMOTE_SERVICE_JNI_KEYS_H_
diff --git a/services/core/jni/com_android_server_tv_TvUinputBridge.cpp b/services/core/jni/com_android_server_tv_TvUinputBridge.cpp
index c832c18..0e96bd7 100644
--- a/services/core/jni/com_android_server_tv_TvUinputBridge.cpp
+++ b/services/core/jni/com_android_server_tv_TvUinputBridge.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "TvRemote-native-uiBridge"
+#include "com_android_server_tv_GamepadKeys.h"
#include "com_android_server_tv_TvKeys.h"
#include "jni.h"
@@ -92,27 +93,156 @@
}
}
+static const int kInvalidFileDescriptor = -1;
+
+// Convenience class to manage an opened /dev/uinput device
+class UInputDescriptor {
+public:
+ UInputDescriptor() : mFd(kInvalidFileDescriptor) {
+ memset(&mUinputDescriptor, 0, sizeof(mUinputDescriptor));
+ }
+
+ // Auto-closes any open /dev/uinput descriptor unless detached.
+ ~UInputDescriptor();
+
+ // Open /dev/uinput and prepare to register
+ // the device with the given name and unique Id
+ bool Open(const char* name, const char* uniqueId);
+
+ // Checks if the current file descriptor is valid
+ bool IsValid() const { return mFd != kInvalidFileDescriptor; }
+
+ void EnableKey(int keyCode);
+
+ void EnableAxesEvents();
+ void EnableAxis(int axis, int rangeMin, int rangeMax);
+
+ bool Create();
+
+ // Detaches from the current file descriptor
+ // Returns the file descriptor for /dev/uniput
+ int Detach();
+
+private:
+ int mFd;
+ struct uinput_user_dev mUinputDescriptor;
+};
+
+UInputDescriptor::~UInputDescriptor() {
+ if (mFd != kInvalidFileDescriptor) {
+ close(mFd);
+ mFd = kInvalidFileDescriptor;
+ }
+}
+
+int UInputDescriptor::Detach() {
+ int fd = mFd;
+ mFd = kInvalidFileDescriptor;
+ return fd;
+}
+
+bool UInputDescriptor::Open(const char* name, const char* uniqueId) {
+ if (IsValid()) {
+ ALOGE("UInput device already open");
+ return false;
+ }
+
+ mFd = ::open("/dev/uinput", O_WRONLY | O_NDELAY);
+ if (mFd < 0) {
+ ALOGE("Cannot open /dev/uinput: %s.", strerror(errno));
+ mFd = kInvalidFileDescriptor;
+ return false;
+ }
+
+ // write device unique id to the phys property
+ ioctl(mFd, UI_SET_PHYS, uniqueId);
+
+ memset(&mUinputDescriptor, 0, sizeof(mUinputDescriptor));
+ strlcpy(mUinputDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
+ mUinputDescriptor.id.version = 1;
+ mUinputDescriptor.id.bustype = BUS_VIRTUAL;
+
+ // All UInput devices we use process keys
+ ioctl(mFd, UI_SET_EVBIT, EV_KEY);
+
+ return true;
+}
+
+void UInputDescriptor::EnableKey(int keyCode) {
+ ioctl(mFd, UI_SET_KEYBIT, keyCode);
+}
+
+void UInputDescriptor::EnableAxesEvents() {
+ ioctl(mFd, UI_SET_EVBIT, EV_ABS);
+}
+
+void UInputDescriptor::EnableAxis(int axis, int rangeMin, int rangeMax) {
+ if ((axis < 0) || (axis >= NELEM(mUinputDescriptor.absmin))) {
+ ALOGE("Invalid axis number: %d", axis);
+ return;
+ }
+
+ if (ioctl(mFd, UI_SET_ABSBIT, axis) != 0) {
+ ALOGE("Failed to set absbit for %d", axis);
+ }
+
+ mUinputDescriptor.absmin[axis] = rangeMin;
+ mUinputDescriptor.absmax[axis] = rangeMax;
+ mUinputDescriptor.absfuzz[axis] = 0;
+ mUinputDescriptor.absflat[axis] = 0;
+}
+
+bool UInputDescriptor::Create() {
+ // register the input device
+ if (write(mFd, &mUinputDescriptor, sizeof(mUinputDescriptor)) != sizeof(mUinputDescriptor)) {
+ ALOGE("Cannot write uinput_user_dev to fd %d: %s.", mFd, strerror(errno));
+ return false;
+ }
+
+ if (ioctl(mFd, UI_DEV_CREATE) != 0) {
+ ALOGE("Unable to create uinput device: %s.", strerror(errno));
+ return false;
+ }
+
+ ALOGV("Created uinput device, fd=%d.", mFd);
+
+ return true;
+}
+
class NativeConnection {
public:
+ enum class ConnectionType {
+ kRemoteDevice,
+ kGamepadDevice,
+ };
+
~NativeConnection();
static NativeConnection* open(const char* name, const char* uniqueId,
int32_t width, int32_t height, int32_t maxPointerId);
+ static NativeConnection* openGamepad(const char* name, const char* uniqueId);
+
void sendEvent(int32_t type, int32_t code, int32_t value);
int32_t getMaxPointers() const { return mMaxPointers; }
+ ConnectionType getType() const { return mType; }
+
+ bool IsGamepad() const { return getType() == ConnectionType::kGamepadDevice; }
+
+ bool IsRemote() const { return getType() == ConnectionType::kRemoteDevice; }
+
private:
- NativeConnection(int fd, int32_t maxPointers);
+ NativeConnection(int fd, int32_t maxPointers, ConnectionType type);
const int mFd;
const int32_t mMaxPointers;
+ const ConnectionType mType;
};
-NativeConnection::NativeConnection(int fd, int32_t maxPointers) :
- mFd(fd), mMaxPointers(maxPointers) {
-}
+NativeConnection::NativeConnection(int fd, int32_t maxPointers, ConnectionType type)
+ : mFd(fd), mMaxPointers(maxPointers), mType(type) {}
NativeConnection::~NativeConnection() {
ALOGI("Un-Registering uinput device %d.", mFd);
@@ -125,44 +255,50 @@
ALOGI("Registering uinput device %s: touch pad size %dx%d, "
"max pointers %d.", name, width, height, maxPointers);
- int fd = ::open("/dev/uinput", O_WRONLY | O_NDELAY);
- if (fd < 0) {
- ALOGE("Cannot open /dev/uinput: %s.", strerror(errno));
- return nullptr;
- }
-
- struct uinput_user_dev uinp;
- memset(&uinp, 0, sizeof(struct uinput_user_dev));
- strlcpy(uinp.name, name, UINPUT_MAX_NAME_SIZE);
- uinp.id.version = 1;
- uinp.id.bustype = BUS_VIRTUAL;
-
- // initialize keymap
initKeysMap();
- // write device unique id to the phys property
- ioctl(fd, UI_SET_PHYS, uniqueId);
-
- // set the keys mapped
- ioctl(fd, UI_SET_EVBIT, EV_KEY);
- for (size_t i = 0; i < NELEM(KEYS); i++) {
- ioctl(fd, UI_SET_KEYBIT, KEYS[i].linuxKeyCode);
- }
-
- // register the input device
- if (write(fd, &uinp, sizeof(uinp)) != sizeof(uinp)) {
- ALOGE("Cannot write uinput_user_dev to fd %d: %s.", fd, strerror(errno));
- close(fd);
- return NULL;
- }
- if (ioctl(fd, UI_DEV_CREATE) != 0) {
- ALOGE("Unable to create uinput device: %s.", strerror(errno));
- close(fd);
+ UInputDescriptor descriptor;
+ if (!descriptor.Open(name, uniqueId)) {
return nullptr;
}
- ALOGV("Created uinput device, fd=%d.", fd);
- return new NativeConnection(fd, maxPointers);
+ // set the keys mapped
+ for (size_t i = 0; i < NELEM(KEYS); i++) {
+ descriptor.EnableKey(KEYS[i].linuxKeyCode);
+ }
+
+ if (!descriptor.Create()) {
+ return nullptr;
+ }
+
+ return new NativeConnection(descriptor.Detach(), maxPointers, ConnectionType::kRemoteDevice);
+}
+
+NativeConnection* NativeConnection::openGamepad(const char* name, const char* uniqueId) {
+ ALOGI("Registering uinput device %s: gamepad", name);
+
+ UInputDescriptor descriptor;
+ if (!descriptor.Open(name, uniqueId)) {
+ return nullptr;
+ }
+
+ // set the keys mapped for gamepads
+ for (size_t i = 0; i < NELEM(GAMEPAD_KEY_CODES); i++) {
+ descriptor.EnableKey(GAMEPAD_KEY_CODES[i]);
+ }
+
+ // define the axes that are required
+ descriptor.EnableAxesEvents();
+ for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) {
+ const Axis& axis = GAMEPAD_AXES[i];
+ descriptor.EnableAxis(axis.number, axis.rangeMin, axis.rangeMax);
+ }
+
+ if (!descriptor.Create()) {
+ return nullptr;
+ }
+
+ return new NativeConnection(descriptor.Detach(), 0, ConnectionType::kGamepadDevice);
}
void NativeConnection::sendEvent(int32_t type, int32_t code, int32_t value) {
@@ -174,7 +310,6 @@
write(mFd, &iev, sizeof(iev));
}
-
static jlong nativeOpen(JNIEnv* env, jclass clazz,
jstring nameStr, jstring uniqueIdStr,
jint width, jint height, jint maxPointers) {
@@ -186,6 +321,14 @@
return reinterpret_cast<jlong>(connection);
}
+static jlong nativeGamepadOpen(JNIEnv* env, jclass clazz, jstring nameStr, jstring uniqueIdStr) {
+ ScopedUtfChars name(env, nameStr);
+ ScopedUtfChars uniqueId(env, uniqueIdStr);
+
+ NativeConnection* connection = NativeConnection::openGamepad(name.c_str(), uniqueId.c_str());
+ return reinterpret_cast<jlong>(connection);
+}
+
static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
delete connection;
@@ -194,6 +337,12 @@
static void nativeSendKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyCode, jboolean down) {
int32_t code = getLinuxKeyCode(keyCode);
NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
+
+ if (connection->IsGamepad()) {
+ ALOGE("Invalid key even for a gamepad - need to send gamepad events");
+ return;
+ }
+
if (code != KEY_UNKNOWN) {
connection->sendEvent(EV_KEY, code, down ? 1 : 0);
} else {
@@ -201,10 +350,44 @@
}
}
+static void nativeSendGamepadKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyIndex,
+ jboolean down) {
+ NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
+
+ if (!connection->IsGamepad()) {
+ ALOGE("Invalid gamepad key for non-gamepad device");
+ return;
+ }
+
+ if ((keyIndex < 0) || (keyIndex >= NELEM(GAMEPAD_KEY_CODES))) {
+ ALOGE("Invalid gamepad key index: %d", keyIndex);
+ return;
+ }
+
+ connection->sendEvent(EV_KEY, GAMEPAD_KEY_CODES[keyIndex], down ? 1 : 0);
+}
+
+static void nativeSendGamepadAxisValue(JNIEnv* env, jclass clazz, jlong ptr, jint axis,
+ jint value) {
+ NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
+
+ if (!connection->IsGamepad()) {
+ ALOGE("Invalid axis send for non-gamepad device");
+ return;
+ }
+
+ connection->sendEvent(EV_ABS, axis, value);
+}
+
static void nativeSendPointerDown(JNIEnv* env, jclass clazz, jlong ptr,
jint pointerId, jint x, jint y) {
NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
+ if (connection->IsGamepad()) {
+ ALOGE("Invalid pointer down event for a gamepad.");
+ return;
+ }
+
int32_t slot = findSlot(pointerId);
if (slot == SLOT_UNKNOWN) {
slot = assignSlot(pointerId);
@@ -221,6 +404,11 @@
jint pointerId) {
NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
+ if (connection->IsGamepad()) {
+ ALOGE("Invalid pointer up event for a gamepad.");
+ return;
+ }
+
int32_t slot = findSlot(pointerId);
if (slot != SLOT_UNKNOWN) {
connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
@@ -238,17 +426,34 @@
NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
// Clear keys.
- for (size_t i = 0; i < NELEM(KEYS); i++) {
- connection->sendEvent(EV_KEY, KEYS[i].linuxKeyCode, 0);
- }
+ if (connection->IsRemote()) {
+ for (size_t i = 0; i < NELEM(KEYS); i++) {
+ connection->sendEvent(EV_KEY, KEYS[i].linuxKeyCode, 0);
+ }
- // Clear pointers.
- int32_t slot = SLOT_UNKNOWN;
- for (int32_t i = 0; i < connection->getMaxPointers(); i++) {
- slot = findSlot(i);
- if (slot != SLOT_UNKNOWN) {
- connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
- connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+ // Clear pointers.
+ int32_t slot = SLOT_UNKNOWN;
+ for (int32_t i = 0; i < connection->getMaxPointers(); i++) {
+ slot = findSlot(i);
+ if (slot != SLOT_UNKNOWN) {
+ connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
+ connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+ }
+ }
+ } else {
+ for (size_t i = 0; i < NELEM(GAMEPAD_KEY_CODES); i++) {
+ connection->sendEvent(EV_KEY, GAMEPAD_KEY_CODES[i], 0);
+ }
+
+ for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) {
+ const Axis& axis = GAMEPAD_AXES[i];
+ if ((axis.number == ABS_Z) || (axis.number == ABS_RZ)) {
+ // Mark triggers unpressed
+ connection->sendEvent(EV_ABS, axis.number, 0);
+ } else {
+ // Joysticks and dpad rests on center
+ connection->sendEvent(EV_ABS, axis.number, (axis.rangeMin + axis.rangeMax) / 2);
+ }
}
}
@@ -261,20 +466,16 @@
*/
static JNINativeMethod gUinputBridgeMethods[] = {
- { "nativeOpen", "(Ljava/lang/String;Ljava/lang/String;III)J",
- (void*)nativeOpen },
- { "nativeClose", "(J)V",
- (void*)nativeClose },
- { "nativeSendKey", "(JIZ)V",
- (void*)nativeSendKey },
- { "nativeSendPointerDown", "(JIII)V",
- (void*)nativeSendPointerDown },
- { "nativeSendPointerUp", "(JI)V",
- (void*)nativeSendPointerUp },
- { "nativeClear", "(J)V",
- (void*)nativeClear },
- { "nativeSendPointerSync", "(J)V",
- (void*)nativeSendPointerSync },
+ {"nativeOpen", "(Ljava/lang/String;Ljava/lang/String;III)J", (void*)nativeOpen},
+ {"nativeGamepadOpen", "(Ljava/lang/String;Ljava/lang/String;)J", (void*)nativeGamepadOpen},
+ {"nativeClose", "(J)V", (void*)nativeClose},
+ {"nativeSendKey", "(JIZ)V", (void*)nativeSendKey},
+ {"nativeSendPointerDown", "(JIII)V", (void*)nativeSendPointerDown},
+ {"nativeSendPointerUp", "(JI)V", (void*)nativeSendPointerUp},
+ {"nativeClear", "(J)V", (void*)nativeClear},
+ {"nativeSendPointerSync", "(J)V", (void*)nativeSendPointerSync},
+ {"nativeSendGamepadKey", "(JIZ)V", (void*)nativeSendGamepadKey},
+ {"nativeSendGamepadAxisValue", "(JII)V", (void*)nativeSendGamepadAxisValue},
};
int register_android_server_tv_TvUinputBridge(JNIEnv* env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 312d2d2..6ab5303 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -86,6 +86,7 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Telephony.Carriers.DPC_URI;
@@ -392,6 +393,7 @@
private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
+ private static final long MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD = 3 * MS_PER_DAY;
private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION =
"com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
@@ -785,7 +787,7 @@
List<String> mLockTaskPackages = new ArrayList<>();
// List of packages protected by device owner
- List<String> mProtectedPackages = new ArrayList<>();
+ List<String> mUserControlDisabledPackages = new ArrayList<>();
// Bitfield of feature flags to be enabled during LockTask mode.
// We default on the power button menu, in order to be consistent with pre-P behaviour.
@@ -1074,6 +1076,7 @@
private static final String TAG_PROFILE_OFF_DEADLINE = "profile-off-deadline";
private static final String TAG_ALWAYS_ON_VPN_PACKAGE = "vpn-package";
private static final String TAG_ALWAYS_ON_VPN_LOCKDOWN = "vpn-lockdown";
+ private static final String TAG_COMMON_CRITERIA_MODE = "common-criteria-mode";
DeviceAdminInfo info;
@@ -1198,13 +1201,13 @@
// Whether the admin explicitly requires personal apps to be suspended
boolean mSuspendPersonalApps = false;
// Maximum time the profile owned by this admin can be off.
- long mProfileMaximumTimeOff = 0;
+ long mProfileMaximumTimeOffMillis = 0;
// Time by which the profile should be turned on according to System.currentTimeMillis().
long mProfileOffDeadline = 0;
public String mAlwaysOnVpnPackage;
public boolean mAlwaysOnVpnLockdown;
-
+ boolean mCommonCriteriaMode;
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
@@ -1439,10 +1442,11 @@
if (mSuspendPersonalApps) {
writeAttributeValueToXml(out, TAG_SUSPEND_PERSONAL_APPS, mSuspendPersonalApps);
}
- if (mProfileMaximumTimeOff != 0) {
- writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF, mProfileMaximumTimeOff);
+ if (mProfileMaximumTimeOffMillis != 0) {
+ writeAttributeValueToXml(out, TAG_PROFILE_MAXIMUM_TIME_OFF,
+ mProfileMaximumTimeOffMillis);
}
- if (mProfileMaximumTimeOff != 0) {
+ if (mProfileMaximumTimeOffMillis != 0) {
writeAttributeValueToXml(out, TAG_PROFILE_OFF_DEADLINE, mProfileOffDeadline);
}
if (!TextUtils.isEmpty(mAlwaysOnVpnPackage)) {
@@ -1451,6 +1455,9 @@
if (mAlwaysOnVpnLockdown) {
writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_LOCKDOWN, mAlwaysOnVpnLockdown);
}
+ if (mCommonCriteriaMode) {
+ writeAttributeValueToXml(out, TAG_COMMON_CRITERIA_MODE, mCommonCriteriaMode);
+ }
}
void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
@@ -1691,7 +1698,7 @@
mSuspendPersonalApps = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) {
- mProfileMaximumTimeOff =
+ mProfileMaximumTimeOffMillis =
Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) {
mProfileOffDeadline =
@@ -1701,6 +1708,9 @@
} else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) {
mAlwaysOnVpnLockdown = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_COMMON_CRITERIA_MODE.equals(tag)) {
+ mCommonCriteriaMode = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1929,14 +1939,16 @@
pw.println(mCrossProfilePackages);
pw.print("mSuspendPersonalApps=");
pw.println(mSuspendPersonalApps);
- pw.print("mProfileMaximumTimeOff=");
- pw.println(mProfileMaximumTimeOff);
+ pw.print("mProfileMaximumTimeOffMillis=");
+ pw.println(mProfileMaximumTimeOffMillis);
pw.print("mProfileOffDeadline=");
pw.println(mProfileOffDeadline);
pw.print("mAlwaysOnVpnPackage=");
pw.println(mAlwaysOnVpnPackage);
pw.print("mAlwaysOnVpnLockdown=");
pw.println(mAlwaysOnVpnLockdown);
+ pw.print("mCommonCriteriaMode=");
+ pw.println(mCommonCriteriaMode);
}
}
@@ -3539,8 +3551,8 @@
out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
}
- for (int i = 0, size = policy.mProtectedPackages.size(); i < size; i++) {
- String packageName = policy.mProtectedPackages.get(i);
+ for (int i = 0, size = policy.mUserControlDisabledPackages.size(); i < size; i++) {
+ String packageName = policy.mUserControlDisabledPackages.get(i);
out.startTag(null, TAG_PROTECTED_PACKAGES);
out.attribute(null, ATTR_NAME, packageName);
out.endTag(null, TAG_PROTECTED_PACKAGES);
@@ -3665,7 +3677,7 @@
policy.mAdminMap.clear();
policy.mAffiliationIds.clear();
policy.mOwnerInstalledCaCerts.clear();
- policy.mProtectedPackages.clear();
+ policy.mUserControlDisabledPackages.clear();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3767,7 +3779,8 @@
} else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
} else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
- policy.mProtectedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
+ policy.mUserControlDisabledPackages.add(
+ parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_APPS_SUSPENDED.equals(tag)) {
policy.mAppsSuspended =
Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE));
@@ -3802,7 +3815,7 @@
updateMaximumTimeToLockLocked(userHandle);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
- updateProtectedPackagesLocked(policy.mProtectedPackages);
+ updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages);
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
@@ -3828,7 +3841,7 @@
}
}
- private void updateProtectedPackagesLocked(List<String> packages) {
+ private void updateUserControlDisabledPackagesLocked(List<String> packages) {
mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages);
}
@@ -5874,6 +5887,14 @@
}
}
+ private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) {
+ if (mContext.checkCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ enforceProfileOrDeviceOwner(who);
+ }
+
private void enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(ComponentName who) {
synchronized (getLockObject()) {
getActiveAdminForCallerLocked(
@@ -6870,7 +6891,7 @@
@Override
public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
- enforceProfileOrDeviceOwner(admin);
+ enforceNetworkStackOrProfileOrDeviceOwner(admin);
final int userId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
@@ -7786,7 +7807,7 @@
* Set whether auto time is enabled on the device.
*/
@Override
- public void setAutoTime(ComponentName who, boolean enabled) {
+ public void setAutoTimeEnabled(ComponentName who, boolean enabled) {
if (!mHasFeature) {
return;
}
@@ -7807,7 +7828,7 @@
* Returns whether auto time is used on the device or not.
*/
@Override
- public boolean getAutoTime(ComponentName who) {
+ public boolean getAutoTimeEnabled(ComponentName who) {
if (!mHasFeature) {
return false;
}
@@ -7821,7 +7842,7 @@
* Set whether auto time zone is enabled on the device.
*/
@Override
- public void setAutoTimeZone(ComponentName who, boolean enabled) {
+ public void setAutoTimeZoneEnabled(ComponentName who, boolean enabled) {
if (!mHasFeature) {
return;
}
@@ -7842,7 +7863,7 @@
* Returns whether auto time zone is used on the device or not.
*/
@Override
- public boolean getAutoTimeZone(ComponentName who) {
+ public boolean getAutoTimeZoneEnabled(ComponentName who) {
if (!mHasFeature) {
return false;
}
@@ -8820,8 +8841,8 @@
policy.mLockTaskPackages.clear();
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userId);
policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
- policy.mProtectedPackages.clear();
- updateProtectedPackagesLocked(policy.mProtectedPackages);
+ policy.mUserControlDisabledPackages.clear();
+ updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages);
saveSettingsLocked(userId);
try {
@@ -9574,7 +9595,8 @@
pw.println();
pw.increaseIndent();
pw.print("mPasswordOwner="); pw.println(policy.mPasswordOwner);
- pw.print("mProtectedPackages="); pw.println(policy.mProtectedPackages);
+ pw.print("mUserControlDisabledPackages=");
+ pw.println(policy.mUserControlDisabledPackages);
pw.print("mAppsSuspended="); pw.println(policy.mAppsSuspended);
pw.decreaseIndent();
}
@@ -15549,39 +15571,39 @@
}
@Override
- public void setProtectedPackages(ComponentName who, List<String> packages) {
+ public void setUserControlDisabledPackages(ComponentName who, List<String> packages) {
Preconditions.checkNotNull(who, "ComponentName is null");
Preconditions.checkNotNull(packages, "packages is null");
enforceDeviceOwner(who);
synchronized (getLockObject()) {
final int userHandle = mInjector.userHandleGetCallingUserId();
- setProtectedPackagesLocked(userHandle, packages);
+ setUserControlDisabledPackagesLocked(userHandle, packages);
DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.SET_PACKAGES_PROTECTED)
+ .createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES)
.setAdmin(who)
.setStrings(packages.toArray(new String[packages.size()]))
.write();
}
}
- private void setProtectedPackagesLocked(int userHandle, List<String> packages) {
+ private void setUserControlDisabledPackagesLocked(int userHandle, List<String> packages) {
final DevicePolicyData policy = getUserData(userHandle);
- policy.mProtectedPackages = packages;
+ policy.mUserControlDisabledPackages = packages;
// Store the settings persistently.
saveSettingsLocked(userHandle);
- updateProtectedPackagesLocked(packages);
+ updateUserControlDisabledPackagesLocked(packages);
}
@Override
- public List<String> getProtectedPackages(ComponentName who) {
+ public List<String> getUserControlDisabledPackages(ComponentName who) {
Preconditions.checkNotNull(who, "ComponentName is null");
enforceDeviceOwner(who);
final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
synchronized (getLockObject()) {
- final List<String> packages = getUserData(userHandle).mProtectedPackages;
+ final List<String> packages = getUserData(userHandle).mUserControlDisabledPackages;
return packages == null ? Collections.EMPTY_LIST : packages;
}
}
@@ -15593,28 +15615,38 @@
}
@Override
- public void setCommonCriteriaModeEnabled(ComponentName admin, boolean enabled) {
+ public void setCommonCriteriaModeEnabled(ComponentName who, boolean enabled) {
+ final int userId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(admin,
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+ admin.mCommonCriteriaMode = enabled;
+ saveSettingsLocked(userId);
}
- mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.settingsGlobalPutInt(Settings.Global.COMMON_CRITERIA_MODE,
- enabled ? 1 : 0));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_COMMON_CRITERIA_MODE)
- .setAdmin(admin)
+ .setAdmin(who)
.setBoolean(enabled)
.write();
}
@Override
- public boolean isCommonCriteriaModeEnabled(ComponentName admin) {
- synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+ public boolean isCommonCriteriaModeEnabled(ComponentName who) {
+ if (who != null) {
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+ return admin.mCommonCriteriaMode;
+ }
}
- return mInjector.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0) != 0;
+ // Return aggregated state if caller is not admin (who == null).
+ synchronized (getLockObject()) {
+ // Only DO or COPE PO can turn on CC mode, so take a shortcut here and only look at
+ // their ActiveAdmin, instead of iterating through all admins.
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return admin != null ? admin.mCommonCriteriaMode : false;
+ }
}
@Override
@@ -15716,18 +15748,18 @@
}
boolean shouldSaveSettings = false;
if (profileOwner.mProfileOffDeadline != 0
- && (profileOwner.mProfileMaximumTimeOff == 0 || unlocked)) {
+ && (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) {
// There is a deadline but either there is no policy or the profile is unlocked -> clear
// the deadline.
Slog.i(LOG_TAG, "Profile off deadline is reset to zero");
profileOwner.mProfileOffDeadline = 0;
shouldSaveSettings = true;
} else if (profileOwner.mProfileOffDeadline == 0
- && (profileOwner.mProfileMaximumTimeOff != 0 && !unlocked)) {
+ && (profileOwner.mProfileMaximumTimeOffMillis != 0 && !unlocked)) {
// There profile is locked and there is a policy, but the deadline is not set -> set the
// deadline.
Slog.i(LOG_TAG, "Profile off deadline is set.");
- profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOff;
+ profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOffMillis;
shouldSaveSettings = true;
}
@@ -15828,7 +15860,7 @@
}
@Override
- public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMs) {
+ public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMillis) {
final int userId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
@@ -15837,10 +15869,16 @@
// DO shouldn't be able to use this method.
enforceProfileOwnerOfOrganizationOwnedDevice(admin);
enforceHandlesCheckPolicyComplianceIntent(userId, admin.info.getPackageName());
- if (admin.mProfileMaximumTimeOff == timeoutMs) {
+ Preconditions.checkArgument(timeoutMillis >= 0, "Timeout must be non-negative.");
+ // Ensure the timeout is long enough to avoid having bad user experience.
+ if (timeoutMillis > 0 && timeoutMillis < MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD
+ && !isAdminTestOnlyLocked(who, userId)) {
+ timeoutMillis = MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD;
+ }
+ if (admin.mProfileMaximumTimeOffMillis == timeoutMillis) {
return;
}
- admin.mProfileMaximumTimeOff = timeoutMs;
+ admin.mProfileMaximumTimeOffMillis = timeoutMillis;
saveSettingsLocked(userId);
}
@@ -15850,7 +15888,7 @@
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF)
.setAdmin(who)
- .setTimePeriod(timeoutMs)
+ .setTimePeriod(timeoutMillis)
.write();
}
@@ -15874,7 +15912,7 @@
false /* parent */);
// DO shouldn't be able to use this method.
enforceProfileOwnerOfOrganizationOwnedDevice(admin);
- return admin.mProfileMaximumTimeOff;
+ return admin.mProfileMaximumTimeOffMillis;
}
}
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 3fcb57a..2dbbc5a 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -178,15 +178,9 @@
nfp.size = params.size;
nfp.metadata = {(const char*)params.metadata.data(), (IncFsSize)params.metadata.size()};
if (!params.signature) {
- nfp.verification = {};
+ nfp.signature = {};
} else {
- nfp.verification.hashAlgorithm = IncFsHashAlgortithm(params.signature->hashAlgorithm);
- nfp.verification.rootHash = {(const char*)params.signature->rootHash.data(),
- (IncFsSize)params.signature->rootHash.size()};
- nfp.verification.additionalData = {(const char*)params.signature->additionalData.data(),
- (IncFsSize)params.signature->additionalData.size()};
- nfp.verification.signature = {(const char*)params.signature->signature.data(),
- (IncFsSize)params.signature->signature.size()};
+ nfp.signature = {(const char*)params.signature->data(), (IncFsSize)params.signature->size()};
}
return {0, id, nfp};
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 9c9fe8c..4da0091 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -28,6 +28,7 @@
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
#include <binder/BinderService.h>
+#include <binder/Nullable.h>
#include <binder/ParcelFileDescriptor.h>
#include <binder/Status.h>
#include <sys/stat.h>
@@ -1083,13 +1084,15 @@
return false;
}
FileSystemControlParcel fsControlParcel;
- fsControlParcel.incremental = std::make_unique<IncrementalFileSystemControlParcel>();
+ fsControlParcel.incremental = aidl::make_nullable<IncrementalFileSystemControlParcel>();
fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd)));
fsControlParcel.incremental->pendingReads.reset(
base::unique_fd(::dup(ifs.control.pendingReads)));
fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs)));
sp<IncrementalDataLoaderListener> listener =
- new IncrementalDataLoaderListener(*this, *externalListener);
+ new IncrementalDataLoaderListener(*this,
+ externalListener ? *externalListener
+ : DataLoaderStatusListener());
bool created = false;
auto status = mDataLoaderManager->initializeDataLoader(ifs.mountId, *dlp, fsControlParcel,
listener, &created);
@@ -1154,7 +1157,7 @@
// Create new lib file without signature info
incfs::NewFileParams libFileParams{};
libFileParams.size = uncompressedLen;
- libFileParams.verification.hashAlgorithm = INCFS_HASH_NONE;
+ libFileParams.signature = {};
// Metadata of the new lib file is its relative path
IncFsSpan libFileMetadata;
libFileMetadata.data = targetLibPath.c_str();
@@ -1229,8 +1232,8 @@
std::unique_lock l(incrementalService.mLock);
const auto& ifs = incrementalService.getIfsLocked(mountId);
if (!ifs) {
- LOG(WARNING) << "Received data loader status " << int(newStatus) << " for unknown mount "
- << mountId;
+ LOG(WARNING) << "Received data loader status " << int(newStatus)
+ << " for unknown mount " << mountId;
return binder::Status::ok();
}
ifs->dataLoaderStatus = newStatus;
diff --git a/services/net/Android.bp b/services/net/Android.bp
index dbc2df8..c54102f 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -20,6 +20,44 @@
],
}
+// Version of services.net for usage by the wifi mainline module.
+// Note: This is compiled against module_current.
+// TODO(b/145825329): This should be moved to networkstack-client,
+// with dependencies moved to frameworks/libs/net right.
+java_library {
+ name: "services.net-module-wifi",
+ srcs: [
+ ":framework-services-net-module-wifi-shared-srcs",
+ ":net-module-utils-srcs",
+ "java/android/net/ip/IpClientCallbacks.java",
+ "java/android/net/ip/IpClientManager.java",
+ "java/android/net/ip/IpClientUtil.java",
+ "java/android/net/util/KeepalivePacketDataUtil.java",
+ "java/android/net/util/NetworkConstants.java",
+ "java/android/net/IpMemoryStore.java",
+ "java/android/net/NetworkMonitorManager.java",
+ "java/android/net/TcpKeepalivePacketData.java",
+ ],
+ sdk_version: "module_current",
+ libs: [
+ "unsupportedappusage",
+ ],
+ static_libs: [
+ "dnsresolver_aidl_interface-V2-java",
+ "netd_aidl_interface-unstable-java",
+ "netlink-client",
+ "networkstack-client",
+ "net-utils-services-common",
+ ],
+ apex_available: [
+ "com.android.wifi",
+ ],
+ visibility: [
+ "//frameworks/opt/net/wifi/service",
+ "//frameworks/opt/net/wifi/tests/wifitests",
+ ],
+}
+
filegroup {
name: "services-tethering-shared-srcs",
srcs: [
diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java
index dcefb53..8df2e0d 100644
--- a/services/net/java/android/net/IpMemoryStore.java
+++ b/services/net/java/android/net/IpMemoryStore.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.net.networkstack.ModuleNetworkStackClient;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -41,7 +42,7 @@
super(context);
mService = new CompletableFuture<>();
mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService);
- getNetworkStackClient().fetchIpMemoryStore(
+ getModuleNetworkStackClient(context).fetchIpMemoryStore(
new IIpMemoryStoreCallbacks.Stub() {
@Override
public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) {
@@ -85,8 +86,8 @@
}
@VisibleForTesting
- protected NetworkStackClient getNetworkStackClient() {
- return NetworkStackClient.getInstance();
+ protected ModuleNetworkStackClient getModuleNetworkStackClient(Context context) {
+ return ModuleNetworkStackClient.getInstance(context);
}
/** Gets an instance of the memory store */
diff --git a/services/net/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java
index aad75ae..fcf3a56 100644
--- a/services/net/java/android/net/TcpKeepalivePacketData.java
+++ b/services/net/java/android/net/TcpKeepalivePacketData.java
@@ -74,6 +74,19 @@
ipTtl = tcpDetails.ttl;
}
+ private TcpKeepalivePacketData(final InetAddress srcAddress, int srcPort,
+ final InetAddress dstAddress, int dstPort, final byte[] data, int tcpSeq,
+ int tcpAck, int tcpWnd, int tcpWndScale, int ipTos, int ipTtl)
+ throws InvalidPacketException {
+ super(srcAddress, srcPort, dstAddress, dstPort, data);
+ this.tcpSeq = tcpSeq;
+ this.tcpAck = tcpAck;
+ this.tcpWnd = tcpWnd;
+ this.tcpWndScale = tcpWndScale;
+ this.ipTos = ipTos;
+ this.ipTtl = ipTtl;
+ }
+
/**
* Factory method to create tcp keepalive packet structure.
*/
@@ -169,7 +182,11 @@
/** Write to parcel. */
public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
+ out.writeString(srcAddress.getHostAddress());
+ out.writeString(dstAddress.getHostAddress());
+ out.writeInt(srcPort);
+ out.writeInt(dstPort);
+ out.writeByteArray(getPacket());
out.writeInt(tcpSeq);
out.writeInt(tcpAck);
out.writeInt(tcpWnd);
@@ -178,21 +195,32 @@
out.writeInt(ipTtl);
}
- private TcpKeepalivePacketData(Parcel in) {
- super(in);
- tcpSeq = in.readInt();
- tcpAck = in.readInt();
- tcpWnd = in.readInt();
- tcpWndScale = in.readInt();
- ipTos = in.readInt();
- ipTtl = in.readInt();
+ private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException {
+ InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString());
+ InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString());
+ int srcPort = in.readInt();
+ int dstPort = in.readInt();
+ byte[] packet = in.createByteArray();
+ int tcpSeq = in.readInt();
+ int tcpAck = in.readInt();
+ int tcpWnd = in.readInt();
+ int tcpWndScale = in.readInt();
+ int ipTos = in.readInt();
+ int ipTtl = in.readInt();
+ return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq,
+ tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl);
}
/** Parcelable Creator. */
public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
new Parcelable.Creator<TcpKeepalivePacketData>() {
public TcpKeepalivePacketData createFromParcel(Parcel in) {
- return new TcpKeepalivePacketData(in);
+ try {
+ return readFromParcel(in);
+ } catch (InvalidPacketException e) {
+ throw new IllegalArgumentException(
+ "Invalid NAT-T keepalive data: " + e.error);
+ }
}
public TcpKeepalivePacketData[] newArray(int size) {
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index a3618b4..b329aee 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -22,7 +22,7 @@
import android.net.DhcpResultsParcelable;
import android.net.Layer2PacketParcelable;
import android.net.LinkProperties;
-import android.net.NetworkStackClient;
+import android.net.networkstack.ModuleNetworkStackClient;
import android.os.ConditionVariable;
import java.io.FileDescriptor;
@@ -75,11 +75,11 @@
*
* <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
* {@link IIpClientCallbacks}.
- * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
+ * @see {@link ModuleNetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
*/
public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
- // TODO: migrate clients and remove context argument
- NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback));
+ ModuleNetworkStackClient.getInstance(context)
+ .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
}
/**
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 602e4e1..25ab5d3 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -27,7 +27,7 @@
"services.net",
],
- libs: ["ike-stubs"],
+ libs: ["android.net.ipsec.ike.stubs.system"],
}
//##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 5160eae..32587ac 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -29,7 +29,7 @@
"services.net",
],
- libs: ["ike-stubs"],
+ libs: ["android.net.ipsec.ike.stubs.system"],
}
//##################################################################
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 84421ef..77b5b61 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -166,7 +166,7 @@
PowerManager powerManager =
(PowerManager) application.getSystemService(Context.POWER_SERVICE);
return new UserBackupManagerService.BackupWakeLock(
- powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"));
+ powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"), 0);
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index c94bb87..5c82200 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -79,6 +79,7 @@
private static final String CALLING_PACKAGE2 = "com.package.name2";
private static final String NAMESPACE1 = "namespace1";
private static final String NAMESPACE2 = "namespace2";
+ private static final String DISABLE_RESCUE_PARTY_FLAG = "disable_rescue_party";
private MockitoSession mSession;
private HashMap<String, String> mSystemSettingsMap;
@@ -316,6 +317,13 @@
@Test
public void testExplicitlyEnablingAndDisablingRescue() {
+ // mock the DeviceConfig get call to avoid hitting
+ // android.permission.READ_DEVICE_CONFIG when calling real DeviceConfig.
+ doReturn(true)
+ .when(() -> DeviceConfig.getBoolean(
+ eq(DeviceConfig.NAMESPACE_CONFIGURATION),
+ eq(DISABLE_RESCUE_PARTY_FLAG),
+ eq(false)));
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
@@ -327,6 +335,22 @@
}
@Test
+ public void testDisablingRescueByDeviceConfigFlag() {
+ doReturn(true)
+ .when(() -> DeviceConfig.getBoolean(
+ eq(DeviceConfig.NAMESPACE_CONFIGURATION),
+ eq(DISABLE_RESCUE_PARTY_FLAG),
+ eq(false)));
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
+
+ assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false);
+
+ // Restore the property value initalized in SetUp()
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+ }
+
+ @Test
public void testHealthCheckLevels() {
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index c45ee7b..efe8119 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -127,11 +127,14 @@
mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal());
mAms.mPackageManagerInt = mPackageManagerInt;
doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ // Remove stale instance of PackageManagerInternal if there is any
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
}
@After
public void tearDown() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
mHandlerThread.quit();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index d764a79..7a175ca1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -94,6 +94,7 @@
import com.android.server.wm.ActivityTaskManagerService;
import com.android.server.wm.WindowProcessController;
+import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -141,6 +142,8 @@
sPackageManagerInternal = mock(PackageManagerInternal.class);
doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
.getSystemUiServiceComponent();
+ // Remove stale instance of PackageManagerInternal if there is any
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
sService = mock(ActivityManagerService.class);
@@ -151,6 +154,8 @@
sService.mConstants = new ActivityManagerConstants(sContext, sService,
sContext.getMainThreadHandler());
+ setFieldValue(ActivityManagerService.class, sService, "mContext",
+ sContext);
ProcessList pr = new ProcessList();
pr.init(sService, new ActiveUids(sService, false), null);
setFieldValue(ActivityManagerService.class, sService, "mProcessList",
@@ -170,6 +175,11 @@
sService.mOomAdjuster.mAdjSeq = 10000;
}
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ }
+
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
try {
Field field = clazz.getDeclaredField(fieldName);
@@ -982,7 +992,7 @@
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
doReturn(null).when(sService).getTopAppLocked();
- assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
+ assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
SCHED_GROUP_DEFAULT);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 959dc05..de6f55b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -147,7 +147,7 @@
AndroidPackage mockMyPkg = mock(AndroidPackage.class);
when(mockMyPkg.isPrivileged()).thenReturn(false);
when(mockMyPkg.getUid()).thenReturn(mMyUid);
- when(mockMyPkg.getFeatures()).thenReturn(Collections.emptyList());
+ when(mockMyPkg.getAttributions()).thenReturn(Collections.emptyList());
when(mockPackageManagerInternal.getPackage(sMyPackageName)).thenReturn(mockMyPkg);
doReturn(mockPackageManagerInternal).when(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 164ee31..3ad9054 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -182,7 +182,8 @@
eq(0),
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
- eq(TEST_PACKAGE_NAME));
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
}
@Test
@@ -264,7 +265,8 @@
eq(BiometricAuthenticator.TYPE_FACE),
eq(false) /* requireConfirmation */,
anyInt() /* userId */,
- eq(TEST_PACKAGE_NAME));
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
}
@Test
@@ -391,7 +393,8 @@
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
- eq(TEST_PACKAGE_NAME));
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
// Hardware authenticated
mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
@@ -406,7 +409,8 @@
// SystemUI sends callback with dismissed reason
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED,
+ null /* credentialAttestation */);
waitForIdle();
// HAT sent to keystore
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
@@ -438,7 +442,8 @@
eq(0 /* biometricModality */),
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
- eq(TEST_PACKAGE_NAME));
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
}
@Test
@@ -460,7 +465,8 @@
// SystemUI sends confirm, HAT is sent to keystore and client is notified.
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
+ null /* credentialAttestation */);
waitForIdle();
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
verify(mReceiver1).onAuthenticationSucceeded(
@@ -567,7 +573,8 @@
anyInt(),
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
- anyString());
+ anyString(),
+ anyLong() /* sessionId */);
}
@Test
@@ -627,8 +634,8 @@
verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
// SystemUI animation completed, client is notified, auth session is over
- mBiometricService.mInternalReceiver
- .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+ mBiometricService.mInternalReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_ERROR, null /* credentialAttestation */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -667,7 +674,8 @@
eq(0 /* biometricModality */),
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
- eq(TEST_PACKAGE_NAME));
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
}
@Test
@@ -825,8 +833,8 @@
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
false /* requireConfirmation */, null /* authenticators */);
- mBiometricService.mInternalReceiver
- .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ mBiometricService.mInternalReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle();
verify(mReceiver1).onError(
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -854,7 +862,7 @@
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */);
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ BiometricPrompt.DISMISSED_REASON_NEGATIVE, null /* credentialAttestation */);
waitForIdle();
verify(mBiometricService.mAuthenticators.get(0).impl,
@@ -880,7 +888,7 @@
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
0 /* vendorCode */);
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle();
verify(mBiometricService.mAuthenticators.get(0).impl,
@@ -903,7 +911,7 @@
true /* requireConfirmation */,
new byte[69] /* HAT */);
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
waitForIdle();
// doesn't send cancel to HAL
@@ -1160,7 +1168,8 @@
eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */),
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
- eq(TEST_PACKAGE_NAME));
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
// Requesting strong and credential, when credential is setup
resetReceiver();
@@ -1179,7 +1188,8 @@
eq(BiometricAuthenticator.TYPE_NONE /* biometricModality */),
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
- eq(TEST_PACKAGE_NAME));
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
// Un-downgrading the authenticator allows successful strong auth
for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) {
@@ -1201,7 +1211,8 @@
eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */),
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
- eq(TEST_PACKAGE_NAME));
+ eq(TEST_PACKAGE_NAME),
+ anyLong() /* sessionId */);
}
@Test(expected = IllegalStateException.class)
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 2944643..8be9213 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -251,6 +251,28 @@
}
@Test
+ public void testAllowRemoveOverrideNoOverride() throws Exception {
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L)
+ .addLoggingOnlyChangeWithId(2L)
+ .build();
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package")
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ // Reject all override attempts.
+ // Force the validator to prevent overriding the change by using a user build.
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+ // Try to remove a non existing override, and it doesn't fail.
+ assertThat(compatConfig.removeOverride(1234L, "com.some.package")).isFalse();
+ assertThat(compatConfig.removeOverride(2L, "com.some.package")).isFalse();
+ compatConfig.removePackageOverrides("com.some.package");
+ }
+
+ @Test
public void testRemovePackageOverride() throws Exception {
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
.addEnabledChangeWithId(1234L)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 53b90f2..72d8525 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -30,10 +30,12 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.os.Build;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.LocalServices;
import org.junit.Before;
@@ -78,6 +80,47 @@
}
@Test
+ public void testListAllChanges() {
+ mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(1L)
+ .addDisabledChangeWithIdAndName(2L, "change2")
+ .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
+ .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+ .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+ .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+ .addLoggingOnlyChangeWithId(7L)
+ .build();
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
+ new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
+ new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
+ new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, false, false,
+ "description"),
+ new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
+ new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""),
+ new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, false, false, ""),
+ new CompatibilityChangeInfo(7L, "", -1, false, true, ""));
+ }
+
+ public void testListUIChanges() {
+ mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(1L)
+ .addDisabledChangeWithIdAndName(2L, "change2")
+ .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
+ .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+ .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+ .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+ .addLoggingOnlyChangeWithId(7L)
+ .build();
+ mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+ assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
+ new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
+ new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
+ new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
+ new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""));
+ }
+
+ @Test
public void testRegisterListenerToSameIdThrows() throws Exception {
// Registering a listener to change 1 is successful.
mPlatformCompat.registerListener(1, mListener1);
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 2a6a24e..f57b5f2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3872,79 +3872,80 @@
Settings.System.SCREEN_BRIGHTNESS, "0", DpmMockContext.CALLER_USER_HANDLE);
}
- public void testSetAutoTimeModifiesSetting() throws Exception {
+ public void testSetAutoTimeEnabledModifiesSetting() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
- dpm.setAutoTime(admin1, true);
+ dpm.setAutoTimeEnabled(admin1, true);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1);
- dpm.setAutoTime(admin1, false);
+ dpm.setAutoTimeEnabled(admin1, false);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
}
- public void testSetAutoTimeWithPOOnUser0() throws Exception {
+ public void testSetAutoTimeEnabledWithPOOnUser0() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupProfileOwnerOnUser0();
- dpm.setAutoTime(admin1, true);
+ dpm.setAutoTimeEnabled(admin1, true);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1);
- dpm.setAutoTime(admin1, false);
+ dpm.setAutoTimeEnabled(admin1, false);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
}
- public void testSetAutoTimeFailWithPONotOnUser0() throws Exception {
+ public void testSetAutoTimeEnabledFailWithPONotOnUser0() throws Exception {
setupProfileOwner();
- assertExpectException(SecurityException.class, null, () -> dpm.setAutoTime(admin1, false));
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.setAutoTimeEnabled(admin1, false));
verify(getServices().settings, never()).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
}
- public void testSetAutoTimeWithPOOfOrganizationOwnedDevice() throws Exception {
+ public void testSetAutoTimeEnabledWithPOOfOrganizationOwnedDevice() throws Exception {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
- dpm.setAutoTime(admin1, true);
+ dpm.setAutoTimeEnabled(admin1, true);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1);
- dpm.setAutoTime(admin1, false);
+ dpm.setAutoTimeEnabled(admin1, false);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0);
}
- public void testSetAutoTimeZoneModifiesSetting() throws Exception {
+ public void testSetAutoTimeZoneEnabledModifiesSetting() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
- dpm.setAutoTimeZone(admin1, true);
+ dpm.setAutoTimeZoneEnabled(admin1, true);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
- dpm.setAutoTimeZone(admin1, false);
+ dpm.setAutoTimeZoneEnabled(admin1, false);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
}
- public void testSetAutoTimeZoneWithPOOnUser0() throws Exception {
+ public void testSetAutoTimeZoneEnabledWithPOOnUser0() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupProfileOwnerOnUser0();
- dpm.setAutoTimeZone(admin1, true);
+ dpm.setAutoTimeZoneEnabled(admin1, true);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
- dpm.setAutoTimeZone(admin1, false);
+ dpm.setAutoTimeZoneEnabled(admin1, false);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
}
- public void testSetAutoTimeZoneFailWithPONotOnUser0() throws Exception {
+ public void testSetAutoTimeZoneEnabledFailWithPONotOnUser0() throws Exception {
setupProfileOwner();
assertExpectException(SecurityException.class, null,
- () -> dpm.setAutoTimeZone(admin1, false));
+ () -> dpm.setAutoTimeZoneEnabled(admin1, false));
verify(getServices().settings, never()).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE,
0);
}
- public void testSetAutoTimeZoneWithPOOfOrganizationOwnedDevice() throws Exception {
+ public void testSetAutoTimeZoneEnabledWithPOOfOrganizationOwnedDevice() throws Exception {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
- dpm.setAutoTimeZone(admin1, true);
+ dpm.setAutoTimeZoneEnabled(admin1, true);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 1);
- dpm.setAutoTimeZone(admin1, false);
+ dpm.setAutoTimeZoneEnabled(admin1, false);
verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0);
}
@@ -5842,7 +5843,7 @@
assertTrue(dpm.isPackageAllowedToAccessCalendar(testPackage));
}
- public void testSetProtectedPackages_asDO() throws Exception {
+ public void testSetUserControlDisabledPackages_asDO() throws Exception {
final List<String> testPackages = new ArrayList<>();
testPackages.add("package_1");
testPackages.add("package_2");
@@ -5850,14 +5851,14 @@
mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
setDeviceOwner();
- dpm.setProtectedPackages(admin1, testPackages);
+ dpm.setUserControlDisabledPackages(admin1, testPackages);
verify(getServices().packageManagerInternal).setDeviceOwnerProtectedPackages(testPackages);
- assertEquals(testPackages, dpm.getProtectedPackages(admin1));
+ assertEquals(testPackages, dpm.getUserControlDisabledPackages(admin1));
}
- public void testSetProtectedPackages_failingAsPO() throws Exception {
+ public void testSetUserControlDisabledPackages_failingAsPO() throws Exception {
final List<String> testPackages = new ArrayList<>();
testPackages.add("package_1");
testPackages.add("package_2");
@@ -5866,10 +5867,10 @@
setAsProfileOwner(admin1);
assertExpectException(SecurityException.class, /* messageRegex= */ null,
- () -> dpm.setProtectedPackages(admin1, testPackages));
+ () -> dpm.setUserControlDisabledPackages(admin1, testPackages));
assertExpectException(SecurityException.class, /* messageRegex= */ null,
- () -> dpm.getProtectedPackages(admin1));
+ () -> dpm.getUserControlDisabledPackages(admin1));
}
private void configureProfileOwnerOfOrgOwnedDevice(ComponentName who, int userId) {
@@ -5989,26 +5990,29 @@
public void testSetCommonCriteriaMode_asDeviceOwner() throws Exception {
setDeviceOwner();
- dpm.setCommonCriteriaModeEnabled(admin1, true);
- verify(getServices().settings).settingsGlobalPutInt(
- Settings.Global.COMMON_CRITERIA_MODE, 1);
+ assertFalse(dpm.isCommonCriteriaModeEnabled(admin1));
+ assertFalse(dpm.isCommonCriteriaModeEnabled(null));
- when(getServices().settings.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0))
- .thenReturn(1);
+ dpm.setCommonCriteriaModeEnabled(admin1, true);
+
assertTrue(dpm.isCommonCriteriaModeEnabled(admin1));
+ assertTrue(dpm.isCommonCriteriaModeEnabled(null));
}
public void testSetCommonCriteriaMode_asPoOfOrgOwnedDevice() throws Exception {
- setupProfileOwner();
- configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ assertFalse(dpm.isCommonCriteriaModeEnabled(admin1));
+ assertFalse(dpm.isCommonCriteriaModeEnabled(null));
dpm.setCommonCriteriaModeEnabled(admin1, true);
- verify(getServices().settings).settingsGlobalPutInt(
- Settings.Global.COMMON_CRITERIA_MODE, 1);
- when(getServices().settings.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0))
- .thenReturn(1);
assertTrue(dpm.isCommonCriteriaModeEnabled(admin1));
+ assertTrue(dpm.isCommonCriteriaModeEnabled(null));
}
public void testCanProfileOwnerResetPasswordWhenLocked_nonDirectBootAwarePo()
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 0551f2e..a587029 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -538,6 +538,7 @@
}
@Test
+ @Ignore("b/151150320")
public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSystemAudioModeRequest(
@@ -587,6 +588,7 @@
}
@Test
+ @Ignore("b/151150320")
public void handleRoutingChange_currentActivePortIsHome() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, SELF_PHYSICAL_ADDRESS);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 4a1af51..f72d622 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -139,6 +139,7 @@
}
@Test
+ @Ignore("b/151147315")
public void doNotWakeUpOnHotPlug_PlugIn() {
mWokenUp = false;
mHdmiCecLocalDevicePlayback.onHotplug(0, true);
@@ -146,6 +147,7 @@
}
@Test
+ @Ignore("b/151147315")
public void doNotWakeUpOnHotPlug_PlugOut() {
mWokenUp = false;
mHdmiCecLocalDevicePlayback.onHotplug(0, false);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 3dd1504..d3a3e7e 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -321,6 +322,11 @@
// we cannot check installer cert because it seems to be device specific.
assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
assertFalse(appInstallMetadata.isPreInstalled());
+ // Asserting source stamp not present.
+ assertFalse(appInstallMetadata.isStampPresent());
+ assertFalse(appInstallMetadata.isStampVerified());
+ assertFalse(appInstallMetadata.isStampTrusted());
+ assertNull(appInstallMetadata.getStampCertificateHash());
// These are hardcoded in the test apk android manifest
Map<String, String> allowedInstallers =
appInstallMetadata.getAllowedInstallersAndCertificates();
@@ -474,6 +480,13 @@
assertThat(mService.getCurrentRules().getList()).containsExactly(rule);
}
+ @Test
+ public void getWhitelistedRuleProviders() throws Exception {
+ whitelistUsAsRuleProvider();
+
+ assertThat(mService.getWhitelistedRuleProviders()).containsExactly(TEST_FRAMEWORK_PACKAGE);
+ }
+
private void whitelistUsAsRuleProvider() {
Resources mockResources = mock(Resources.class);
when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
index 70a927c..c5e924b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
@@ -15,16 +15,23 @@
*/
package com.android.server.locksettings;
+import android.content.ContentResolver;
+import android.os.UserHandle;
import android.provider.Settings;
public class FakeSettings {
private int mDeviceProvisioned;
+ private int mSecureFrpMode;
public void setDeviceProvisioned(boolean provisioned) {
mDeviceProvisioned = provisioned ? 1 : 0;
}
+ public void setSecureFrpMode(boolean secure) {
+ mSecureFrpMode = secure ? 1 : 0;
+ }
+
public int globalGetInt(String keyName) {
switch (keyName) {
case Settings.Global.DEVICE_PROVISIONED:
@@ -33,4 +40,12 @@
throw new IllegalArgumentException("Unhandled global settings: " + keyName);
}
}
+
+ public int secureGetInt(ContentResolver contentResolver, String keyName, int defaultValue,
+ int userId) {
+ if (Settings.Secure.SECURE_FRP_MODE.equals(keyName) && userId == UserHandle.USER_SYSTEM) {
+ return mSecureFrpMode;
+ }
+ return defaultValue;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 1ff451b..4e1454b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -124,6 +124,12 @@
}
@Override
+ public int settingsSecureGetInt(ContentResolver contentResolver, String keyName,
+ int defaultValue, int userId) {
+ return mSettings.secureGetInt(contentResolver, keyName, defaultValue, userId);
+ }
+
+ @Override
public UserManagerInternal getUserManagerInternal() {
return mUserManagerInternal;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 684bbd4..661ce11 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -416,6 +416,15 @@
eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
}
+ @Test
+ public void testCredentialChangeNotPossibleInSecureFrpMode() {
+ mSettings.setSecureFrpMode(true);
+ try {
+ mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID);
+ fail("Password shouldn't be changeable before FRP unlock");
+ } catch (SecurityException e) { }
+ }
+
private void testCreateCredential(int userId, LockscreenCredential credential)
throws RemoteException {
assertTrue(mService.setLockCredential(credential, nonePassword(), userId));
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 7e3cfc8..128177b 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1084,7 +1084,7 @@
// pretend that 512 bytes total have happened
stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
+ .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
.thenReturn(stats.getTotalBytes());
@@ -1276,11 +1276,11 @@
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
stats.clear();
- stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.addEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
reset(mNotifManager);
@@ -1304,9 +1304,9 @@
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
stats.clear();
- stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
- stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
reset(mNotifManager);
@@ -1338,7 +1338,7 @@
// bring up wifi network with metered policy
state = new NetworkState[] { buildWifi() };
stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
{
when(mConnManager.getAllNetworkState()).thenReturn(state);
@@ -1769,7 +1769,7 @@
final int CYCLE_DAY = 15;
final NetworkStats stats = new NetworkStats(0L, 1);
- stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+ stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
2999, 1, 2000, 1, 0);
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
.thenReturn(stats.getTotalBytes());
@@ -1793,7 +1793,7 @@
reset(mStatsService);
// Increase the usage.
- stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+ stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
1000, 1, 999, 1, 0);
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
.thenReturn(stats.getTotalBytes());
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 5d5c714..22591c6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -31,6 +31,7 @@
import android.content.pm.Signature;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedProvider;
import android.os.Build;
@@ -135,6 +136,13 @@
.addActivity(activity);
}
+ private static ParsingPackage pkgWithInstrumentation(
+ String packageName, String instrumentationTargetPackage) {
+ ParsedInstrumentation instrumentation = new ParsedInstrumentation();
+ instrumentation.setTargetPackage(instrumentationTargetPackage);
+ return pkg(packageName).addInstrumentation(instrumentation);
+ }
+
private static ParsingPackage pkgWithProvider(String packageName, String authority) {
ParsedProvider provider = new ParsedProvider();
provider.setPackageName(packageName);
@@ -608,6 +616,25 @@
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
+ @Test
+ public void testInstrumentation_DoesntFilter() {
+ final AppsFilter appsFilter =
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
+
+
+ PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
+ DUMMY_TARGET_UID);
+ PackageSetting instrumentation = simulateAddPackage(appsFilter,
+ pkgWithInstrumentation("com.some.other.package", "com.some.package"),
+ DUMMY_CALLING_UID);
+
+ assertFalse(
+ appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, instrumentation, target, 0));
+ assertFalse(
+ appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0));
+ }
+
private interface WithSettingBuilder {
PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 6c769485..6a88298 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -94,6 +94,8 @@
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutUser.PackageWithUser;
+import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.Assert;
@@ -630,6 +632,9 @@
protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
protected ActivityManagerInternal mMockActivityManagerInternal;
protected ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
+ protected UriGrantsManagerInternal mMockUriGrantsManagerInternal;
+
+ protected UriPermissionOwner mUriPermissionOwner;
protected static final String CALLING_PACKAGE_1 = "com.android.test.1";
protected static final int CALLING_UID_1 = 10001;
@@ -771,6 +776,7 @@
mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
mMockActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
+ mMockUriGrantsManagerInternal = mock(UriGrantsManagerInternal.class);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
@@ -782,6 +788,10 @@
LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, mMockUriGrantsManagerInternal);
+
+ mUriPermissionOwner = new UriPermissionOwner(mMockUriGrantsManagerInternal, TAG);
// Prepare injection values.
@@ -2193,6 +2203,7 @@
for (ShortcutInfo s : actualShortcuts) {
assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
+ assertFalse("ID " + s.getId() + " shouldn't have icon URI", s.hasIconUri());
}
return actualShortcuts;
}
@@ -2202,6 +2213,17 @@
for (ShortcutInfo s : actualShortcuts) {
assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
+ assertFalse("ID " + s.getId() + " shouldn't have icon URI", s.hasIconUri());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIconUri(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
+ assertFalse("ID " + s.getId() + " shouldn't have have icon FD", s.hasIconFile());
+ assertTrue("ID " + s.getId() + " not have icon URI", s.hasIconUri());
}
return actualShortcuts;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 56460fb..2cbb6d5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -114,8 +114,11 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -133,6 +136,16 @@
*/
@SmallTest
public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
+
+ @Override
+ protected void tearDown() throws Exception {
+ deleteUriFile("file32x32.jpg");
+ deleteUriFile("file64x64.jpg");
+ deleteUriFile("file512x512.jpg");
+
+ super.tearDown();
+ }
+
/**
* Test for the first launch path, no settings file available.
*/
@@ -724,6 +737,17 @@
final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_512x512));
+ // The corresponding files will be deleted in tearDown()
+ final Icon uri32x32 = Icon.createWithContentUri(
+ getFileUriFromResource("file32x32.jpg", R.drawable.black_32x32));
+ final Icon uri64x64_maskable = Icon.createWithAdaptiveBitmapContentUri(
+ getFileUriFromResource("file64x64.jpg", R.drawable.black_64x64));
+ final Icon uri512x512 = Icon.createWithContentUri(
+ getFileUriFromResource("file512x512.jpg", R.drawable.black_512x512));
+
+ doReturn(mUriPermissionOwner.getExternalToken())
+ .when(mMockUriGrantsManagerInternal).newUriPermissionOwner(anyString());
+
// Set from package 1
setCaller(CALLING_PACKAGE_1);
assertTrue(mManager.setDynamicShortcuts(list(
@@ -732,6 +756,9 @@
makeShortcutWithIcon("bmp32x32", bmp32x32),
makeShortcutWithIcon("bmp64x64", bmp64x64_maskable),
makeShortcutWithIcon("bmp512x512", bmp512x512),
+ makeShortcutWithIcon("uri32x32", uri32x32),
+ makeShortcutWithIcon("uri64x64", uri64x64_maskable),
+ makeShortcutWithIcon("uri512x512", uri512x512),
makeShortcut("none")
)));
@@ -742,6 +769,9 @@
"bmp32x32",
"bmp64x64",
"bmp512x512",
+ "uri32x32",
+ "uri64x64",
+ "uri512x512",
"none");
// Call from another caller with the same ID, just to make sure storage is per-package.
@@ -749,11 +779,15 @@
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("res32x32", res512x512),
makeShortcutWithIcon("res64x64", res512x512),
+ makeShortcutWithIcon("uri32x32", uri512x512),
+ makeShortcutWithIcon("uri64x64", uri512x512),
makeShortcutWithIcon("none", res512x512)
)));
assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
"res32x32",
"res64x64",
+ "uri32x32",
+ "uri64x64",
"none");
// Different profile. Note the names and the contents don't match.
@@ -795,6 +829,18 @@
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0))),
"bmp512x512");
+ assertShortcutIds(assertAllHaveIconUri(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri32x32", USER_0))),
+ "uri32x32");
+
+ assertShortcutIds(assertAllHaveIconUri(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri64x64", USER_0))),
+ "uri64x64");
+
+ assertShortcutIds(assertAllHaveIconUri(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri512x512", USER_0))),
+ "uri512x512");
+
assertShortcutIds(assertAllHaveIconResId(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0))),
"res32x32");
@@ -860,15 +906,37 @@
bmp = pfdToBitmap(
mLauncherApps.getShortcutIconFd(CALLING_PACKAGE_1, "bmp32x32", HANDLE_USER_P0));
assertBitmapSize(128, 128, bmp);
+/*
+ bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri32x32", USER_0)));
+ assertBitmapSize(32, 32, bmp);
- Drawable dr = mLauncherApps.getShortcutIconDrawable(
+ bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri64x64", USER_0)));
+ assertBitmapSize(64, 64, bmp);
+
+ bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri512x512", USER_0)));
+ assertBitmapSize(512, 512, bmp);
+*/
+
+ Drawable dr_bmp = mLauncherApps.getShortcutIconDrawable(
makeShortcutWithIcon("bmp64x64", bmp64x64_maskable), 0);
- assertTrue(dr instanceof AdaptiveIconDrawable);
+ assertTrue(dr_bmp instanceof AdaptiveIconDrawable);
float viewportPercentage = 1 / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
assertEquals((int) (bmp64x64_maskable.getBitmap().getWidth() * viewportPercentage),
- dr.getIntrinsicWidth());
+ dr_bmp.getIntrinsicWidth());
assertEquals((int) (bmp64x64_maskable.getBitmap().getHeight() * viewportPercentage),
- dr.getIntrinsicHeight());
+ dr_bmp.getIntrinsicHeight());
+/*
+ Drawable dr_uri = mLauncherApps.getShortcutIconDrawable(
+ makeShortcutWithIcon("uri64x64", uri64x64_maskable), 0);
+ assertTrue(dr_uri instanceof AdaptiveIconDrawable);
+ assertEquals((int) (bmp64x64_maskable.getBitmap().getWidth() * viewportPercentage),
+ dr_uri.getIntrinsicWidth());
+ assertEquals((int) (bmp64x64_maskable.getBitmap().getHeight() * viewportPercentage),
+ dr_uri.getIntrinsicHeight());
+*/
}
public void testCleanupDanglingBitmaps() throws Exception {
@@ -1274,6 +1342,18 @@
makeShortcut("s1")
)));
+ // Set uri icon
+ assertTrue(mManager.updateShortcuts(list(
+ new ShortcutInfo.Builder(mClientContext, "s1")
+ .setIcon(Icon.createWithContentUri("test_uri"))
+ .build()
+ )));
+ mService.waitForBitmapSavesForTest();
+ assertWith(getCallerShortcuts())
+ .forShortcutWithId("s1", si -> {
+ assertTrue(si.hasIconUri());
+ assertEquals("test_uri", si.getIconUri());
+ });
// Set resource icon
assertTrue(mManager.updateShortcuts(list(
new ShortcutInfo.Builder(mClientContext, "s1")
@@ -1287,6 +1367,9 @@
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
});
mService.waitForBitmapSavesForTest();
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
+
// Set bitmap icon
assertTrue(mManager.updateShortcuts(list(
new ShortcutInfo.Builder(mClientContext, "s1")
@@ -1300,9 +1383,7 @@
assertTrue(si.hasIconFile());
});
- mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
-
- // Do it again, with the reverse order (bitmap -> icon)
+ // Do it again, with the reverse order (bitmap -> resource -> uri)
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1")
)));
@@ -1320,6 +1401,8 @@
assertTrue(si.hasIconFile());
});
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
+
// Set resource icon
assertTrue(mManager.updateShortcuts(list(
new ShortcutInfo.Builder(mClientContext, "s1")
@@ -1332,6 +1415,18 @@
assertTrue(si.hasIconResource());
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
});
+ // Set uri icon
+ assertTrue(mManager.updateShortcuts(list(
+ new ShortcutInfo.Builder(mClientContext, "s1")
+ .setIcon(Icon.createWithContentUri("test_uri"))
+ .build()
+ )));
+ mService.waitForBitmapSavesForTest();
+ assertWith(getCallerShortcuts())
+ .forShortcutWithId("s1", si -> {
+ assertTrue(si.hasIconUri());
+ assertEquals("test_uri", si.getIconUri());
+ });
});
}
@@ -8499,4 +8594,26 @@
}
}
}
+
+ private Uri getFileUriFromResource(String fileName, int resId) throws IOException {
+ File file = new File(getTestContext().getFilesDir(), fileName);
+ // Make sure we are not leaving phantom files behind.
+ assertFalse(file.exists());
+ try (InputStream source = getTestContext().getResources().openRawResource(resId);
+ OutputStream target = new FileOutputStream(file)) {
+ byte[] buffer = new byte[1024];
+ for (int len = source.read(buffer); len >= 0; len = source.read(buffer)) {
+ target.write(buffer, 0, len);
+ }
+ }
+ assertTrue(file.exists());
+ return Uri.fromFile(file);
+ }
+
+ private void deleteUriFile(String fileName) {
+ File file = new File(getTestContext().getFilesDir(), fileName);
+ if (file.exists()) {
+ file.delete();
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 7b101c7..ca77049 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -257,6 +257,7 @@
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
si.setIconResourceId(456);
+ si.setIconUri("test_uri");
si = parceled(si);
@@ -279,6 +280,7 @@
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
+ assertEquals("test_uri", si.getIconUri());
assertEquals(0, si.getTitleResId());
assertEquals(null, si.getTitleResName());
@@ -310,6 +312,7 @@
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
si.setIconResourceId(456);
+ si.setIconUri("test_uri");
lookupAndFillInResourceNames(si);
@@ -335,6 +338,7 @@
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
+ assertEquals("test_uri", si.getIconUri());
}
public void testShortcutInfoClone() {
@@ -359,6 +363,7 @@
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
+ sorig.setIconUri("test_uri");
lookupAndFillInResourceNames(sorig);
@@ -386,6 +391,7 @@
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
+ assertEquals("test_uri", si.getIconUri());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
@@ -407,6 +413,7 @@
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -428,6 +435,7 @@
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -450,6 +458,7 @@
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY
| ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -474,6 +483,7 @@
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -499,6 +509,7 @@
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
+ sorig.setIconUri("test_uri");
lookupAndFillInResourceNames(sorig);
@@ -526,6 +537,7 @@
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
+ assertEquals("test_uri", si.getIconUri());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
@@ -547,6 +559,7 @@
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -570,6 +583,7 @@
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -593,6 +607,7 @@
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -657,6 +672,7 @@
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
+ sorig.setIconUri("test_uri");
lookupAndFillInResourceNames(sorig);
@@ -677,6 +693,7 @@
assertEquals(0, si.getIconResourceId());
assertEquals(null, si.getIconResName());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -787,6 +804,7 @@
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
+ sorig.setIconUri("test_uri");
ShortcutInfo si;
@@ -804,6 +822,7 @@
assertEquals(0, si.getIconResourceId());
assertEquals(null, si.getIconResName());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -977,6 +996,7 @@
| ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
assertNotNull(si.getBitmapPath()); // Something should be set.
assertEquals(0, si.getIconResourceId());
+ assertNull(si.getIconUri());
assertTrue(si.getLastChangedTimestamp() < now);
// Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts
@@ -1053,6 +1073,7 @@
si.getFlags());
assertNotNull(si.getBitmapPath()); // Something should be set.
assertEquals(0, si.getIconResourceId());
+ assertNull(si.getIconUri());
assertTrue(si.getLastChangedTimestamp() < now);
dumpUserFile(USER_10);
@@ -1125,6 +1146,7 @@
assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_RES
| ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
assertNull(si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
assertTrue(si.getLastChangedTimestamp() < now);
@@ -1134,6 +1156,94 @@
assertEquals(1, si.getRank());
}
+ public void testShortcutInfoSaveAndLoad_uri() throws InterruptedException {
+ mRunningUsers.put(USER_10, true);
+
+ setCaller(CALLING_PACKAGE_1, USER_10);
+
+ final Icon uriIcon = Icon.createWithContentUri("test_uri");
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(uriIcon)
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ sorig.setTimestamp(mInjectedCurrentTimeMillis);
+
+ final Icon uriMaskableIcon = Icon.createWithAdaptiveBitmapContentUri("uri_maskable");
+
+ ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("id2")
+ .setTitle("x")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(456)
+ .setIcon(uriMaskableIcon)
+ .build();
+ sorig2.setTimestamp(mInjectedCurrentTimeMillis);
+
+ mManager.addDynamicShortcuts(list(sorig, sorig2));
+
+ mInjectedCurrentTimeMillis += 1;
+ final long now = mInjectedCurrentTimeMillis;
+ mInjectedCurrentTimeMillis += 1;
+
+ // Save and load.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_10);
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
+
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals(10, si.getTitleResId());
+ assertEquals("r10", si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals("r11", si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals("r12", si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(0, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_URI
+ | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+ assertNull(si.getBitmapPath());
+ assertNull(si.getIconResName());
+ assertEquals(0, si.getIconResourceId());
+ assertEquals("test_uri", si.getIconUri());
+ assertTrue(si.getLastChangedTimestamp() < now);
+
+ // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts
+ // to test it.
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10);
+ assertEquals(1, si.getRank());
+ assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_URI
+ | ShortcutInfo.FLAG_STRINGS_RESOLVED | ShortcutInfo.FLAG_ADAPTIVE_BITMAP,
+ si.getFlags());
+ assertNull(si.getBitmapPath());
+ assertNull(si.getIconResName());
+ assertEquals(0, si.getIconResourceId());
+ assertEquals("uri_maskable", si.getIconUri());
+ }
+
public void testShortcutInfoSaveAndLoad_forBackup() {
setCaller(CALLING_PACKAGE_1, USER_0);
@@ -1196,6 +1306,7 @@
| ShortcutInfo.FLAG_SHADOW , si.getFlags());
assertNull(si.getBitmapPath()); // No icon.
assertEquals(0, si.getIconResourceId());
+ assertNull(si.getIconUri());
// Note when restored from backup, it's no longer dynamic, so shouldn't have a rank.
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0);
@@ -1265,6 +1376,77 @@
assertNull(si.getBitmapPath()); // No icon.
assertEquals(0, si.getIconResourceId());
assertEquals(null, si.getIconResName());
+ assertNull(si.getIconUri());
+
+ // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank.
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0);
+ assertEquals(0, si.getRank());
+ }
+
+ public void testShortcutInfoSaveAndLoad_forBackup_uri() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final Icon uriIcon = Icon.createWithContentUri("test_uri");
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(uriIcon)
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+
+ ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("id2")
+ .setTitle("x")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(456)
+ .build();
+
+ mManager.addDynamicShortcuts(list(sorig, sorig2));
+
+ // Dynamic shortcuts won't be backed up, so we need to pin it.
+ setCaller(LAUNCHER_1, USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id", "id2"), HANDLE_USER_0);
+
+ // Do backup & restore.
+ backupAndRestore();
+
+ mService.handleUnlockUser(USER_0); // Load user-0.
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals(10, si.getTitleResId());
+ assertEquals("r10", si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals("r11", si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals("r12", si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(0, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED
+ | ShortcutInfo.FLAG_SHADOW , si.getFlags());
+ assertNull(si.getBitmapPath()); // No icon.
+ assertEquals(0, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+ assertNull(si.getIconUri());
// Note when restored from backup, it's no longer dynamic, so shouldn't have a rank.
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 8da3bdf..155c6dd 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -28,6 +28,8 @@
import android.media.tv.tuner.frontend.FrontendSettings;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -92,6 +94,13 @@
}
};
+ private static int getResourceIdFromHandle(int resourceHandle) {
+ if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return resourceHandle;
+ }
+ return (resourceHandle & 0x00ff0000) >> 16;
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -229,14 +238,15 @@
public void requestFrontendTest_ClientNotRegistered() {
TunerFrontendRequest request =
new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isFalse();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+ assertThat(getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
}
@Test
@@ -256,14 +266,15 @@
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isFalse();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+ assertThat(getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
}
@Test
@@ -287,14 +298,14 @@
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(0);
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(0);
}
@Test
@@ -322,26 +333,26 @@
new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
TunerFrontendRequest request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[1].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].getId())
@@ -382,10 +393,10 @@
TunerFrontendRequest request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -393,8 +404,8 @@
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isFalse();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(listener.isRelaimed()).isFalse();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -403,8 +414,8 @@
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isFalse();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(listener.isRelaimed()).isFalse();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -444,24 +455,24 @@
TunerFrontendRequest request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[1].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -493,14 +504,14 @@
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -515,4 +526,38 @@
assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse();
}
+
+ @Test
+ public void requestDemuxTest() {
+ // Register client
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ int[] demuxHandle = new int[1];
+ TunerDemuxRequest request = new TunerDemuxRequest(clientId[0]);
+ assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle))
+ .isTrue();
+ assertThat(getResourceIdFromHandle(demuxHandle[0])).isEqualTo(0);
+ }
+
+ @Test
+ public void requestDescramblerTest() {
+ // Register client
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ int[] desHandle = new int[1];
+ TunerDescramblerRequest request = new TunerDescramblerRequest(clientId[0]);
+ assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
+ .isTrue();
+ assertThat(getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 7af3ec6..88f368e 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -20,7 +20,7 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -103,7 +103,8 @@
aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_USAGE);
aih.setAppStandbyBucket(PACKAGE_3, USER_ID, 2500, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
aih.setAppStandbyBucket(PACKAGE_4, USER_ID, 2750, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
@@ -114,7 +115,8 @@
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
REASON_MAIN_FORCED_BY_USER);
@@ -133,7 +135,8 @@
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
REASON_MAIN_FORCED_BY_USER);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 387e62d..05cd26d 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -28,6 +28,10 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
@@ -434,6 +438,11 @@
true);
}
+ private int getStandbyBucketReason(String packageName) {
+ return mController.getAppStandbyBucketReason(packageName, USER_ID,
+ mInjector.mElapsedRealtime);
+ }
+
private void assertBucket(int bucket) {
assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
}
@@ -810,7 +819,7 @@
}
@Test
- public void testPredictionRaiseFromRestrictedTimeout() {
+ public void testPredictionRaiseFromRestrictedTimeout_highBucket() {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
// Way past all timeouts. App times out into RESTRICTED bucket.
@@ -823,6 +832,24 @@
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+ }
+
+ @Test
+ public void testPredictionRaiseFromRestrictedTimeout_lowBucket() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+ // Way past all timeouts. App times out into RESTRICTED bucket.
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Prediction into a low bucket means no expectation of the app being used, so we shouldn't
+ // elevate the app from RESTRICTED.
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
}
@Test
@@ -987,6 +1014,79 @@
}
@Test
+ public void testSystemForcedFlags_NotAddedForUserForce() throws Exception {
+ final int expectedReason = REASON_MAIN_FORCED_BY_USER;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1));
+ }
+
+ @Test
+ public void testSystemForcedFlags_AddedForSystemForce() throws Exception {
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_DEFAULT);
+ mInjector.mElapsedRealtime += 4 * RESTRICTED_THRESHOLD;
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+ getStandbyBucketReason(PACKAGE_1));
+
+ mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ // Flags should be combined
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE
+ | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE, getStandbyBucketReason(PACKAGE_1));
+ }
+
+ @Test
+ public void testSystemForcedFlags_SystemForceChangesBuckets() throws Exception {
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_DEFAULT);
+ mInjector.mElapsedRealtime += 4 * RESTRICTED_THRESHOLD;
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
+ assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+ getStandbyBucketReason(PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+ assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ // Flags should be combined
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE
+ | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
+ getStandbyBucketReason(PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ // Flags should be combined
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY,
+ getStandbyBucketReason(PACKAGE_1));
+
+ mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ // Flags should not be combined since the bucket changed.
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED,
+ getStandbyBucketReason(PACKAGE_1));
+ }
+
+ @Test
public void testAddActiveDeviceAdmin() {
assertActiveAdmins(USER_ID, (String[]) null);
assertActiveAdmins(USER_ID2, (String[]) null);
@@ -1108,6 +1208,80 @@
STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
}
+ public void testAppUpdateOnRestrictedBucketStatus() {
+ // Updates shouldn't change bucket if the app timed out.
+ // Way past all timeouts. App times out into RESTRICTED bucket.
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Updates shouldn't change bucket if the app was forced by the system for a non-buggy
+ // reason.
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
+
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Updates should change bucket if the app was forced by the system for a buggy reason.
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
+
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
+ assertNotEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+ // Updates shouldn't change bucket if the app was forced by the system for more than just
+ // a buggy reason.
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
+
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
+ getStandbyBucketReason(PACKAGE_1));
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Updates shouldn't change bucket if the app was forced by the user.
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
new file mode 100644
index 0000000..9636342
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
@@ -0,0 +1,257 @@
+/*
+ * 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.notification;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BubbleCheckerTest extends UiServiceTestCase {
+
+ private static final String SHORTCUT_ID = "shortcut";
+ private static final String PKG = "pkg";
+ private static final String KEY = "key";
+ private static final int USER_ID = 1;
+
+ @Mock
+ ActivityManager mActivityManager;
+ @Mock
+ RankingConfig mRankingConfig;
+ @Mock
+ ShortcutHelper mShortcutHelper;
+
+ @Mock
+ NotificationRecord mNr;
+ @Mock
+ UserHandle mUserHandle;
+ @Mock
+ Notification mNotif;
+ @Mock
+ StatusBarNotification mSbn;
+ @Mock
+ NotificationChannel mChannel;
+ @Mock
+ Notification.BubbleMetadata mBubbleMetadata;
+ @Mock
+ PendingIntent mPendingIntent;
+ @Mock
+ Intent mIntent;
+
+ BubbleExtractor.BubbleChecker mBubbleChecker;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mNr.getKey()).thenReturn(KEY);
+ when(mNr.getSbn()).thenReturn(mSbn);
+ when(mNr.getUser()).thenReturn(mUserHandle);
+ when(mUserHandle.getIdentifier()).thenReturn(USER_ID);
+ when(mNr.getChannel()).thenReturn(mChannel);
+ when(mSbn.getPackageName()).thenReturn(PKG);
+ when(mSbn.getUser()).thenReturn(mUserHandle);
+ when(mNr.getNotification()).thenReturn(mNotif);
+ when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata);
+
+ mBubbleChecker = new BubbleExtractor.BubbleChecker(mContext,
+ mShortcutHelper,
+ mRankingConfig,
+ mActivityManager);
+ }
+
+ void setUpIntentBubble() {
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mBubbleMetadata.getBubbleIntent()).thenReturn(mPendingIntent);
+ when(mBubbleMetadata.getShortcutId()).thenReturn(null);
+ }
+
+ void setUpShortcutBubble(boolean isValid) {
+ when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
+ when(mShortcutHelper.hasValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
+ .thenReturn(isValid);
+ when(mBubbleMetadata.getBubbleIntent()).thenReturn(null);
+ }
+
+ void setUpBubblesEnabled(boolean feature, boolean app, boolean channel) {
+ when(mRankingConfig.bubblesEnabled()).thenReturn(feature);
+ when(mRankingConfig.areBubblesAllowed(PKG, USER_ID)).thenReturn(app);
+ when(mChannel.canBubble()).thenReturn(channel);
+ }
+
+ void setUpActivityIntent(boolean isResizable) {
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ ActivityInfo info = new ActivityInfo();
+ info.resizeMode = isResizable
+ ? RESIZE_MODE_RESIZEABLE
+ : RESIZE_MODE_UNRESIZEABLE;
+ when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(info);
+ }
+
+ //
+ // canBubble
+ //
+
+ @Test
+ public void testCanBubble_true_intentBubble() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ setUpIntentBubble();
+ setUpActivityIntent(true /* isResizable */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_true_shortcutBubble() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ setUpShortcutBubble(true /* isValid */);
+ assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_noIntentInvalidShortcut() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ setUpShortcutBubble(false /* isValid */);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_noIntentNoShortcut() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ when(mBubbleMetadata.getBubbleIntent()).thenReturn(null);
+ when(mBubbleMetadata.getShortcutId()).thenReturn(null);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubbble_false_noMetadata() {
+ setUpBubblesEnabled(true/* feature */, true /* app */, true /* channel */);
+ when(mNotif.getBubbleMetadata()).thenReturn(null);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_bubblesNotEnabled() {
+ setUpBubblesEnabled(false /* feature */, true /* app */, true /* channel */);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_packageNotAllowed() {
+ setUpBubblesEnabled(true /* feature */, false /* app */, true /* channel */);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_channelNotAllowed() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, false /* channel */);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ //
+ // canLaunchInActivityView
+ //
+
+ @Test
+ public void testCanLaunchInActivityView_true() {
+ setUpActivityIntent(true /* resizable */);
+ assertTrue(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+ }
+
+ @Test
+ public void testCanLaunchInActivityView_false_noIntent() {
+ when(mPendingIntent.getIntent()).thenReturn(null);
+ assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+ }
+
+ @Test
+ public void testCanLaunchInActivityView_false_noInfo() {
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(null);
+ assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+ }
+
+ @Test
+ public void testCanLaunchInActivityView_false_notResizable() {
+ setUpActivityIntent(false /* resizable */);
+ assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+ }
+
+ //
+ // isNotificationAppropriateToBubble
+ //
+
+ @Test
+ public void testIsNotifAppropriateToBubble_true() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ setUpIntentBubble();
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpActivityIntent(true /* resizable */);
+ doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
+
+ assertTrue(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+ }
+
+ @Test
+ public void testIsNotifAppropriateToBubble_false_lowRam() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ setUpActivityIntent(true /* resizable */);
+ doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
+
+ assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+ }
+
+ @Test
+ public void testIsNotifAppropriateToBubble_false_notMessageStyle() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpActivityIntent(true /* resizable */);
+ doReturn(Notification.BigPictureStyle.class).when(mNotif).getNotificationStyle();
+
+ assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+ }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
index 7459c4b..c7cef05 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
@@ -21,6 +21,7 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -46,6 +47,7 @@
public class BubbleExtractorTest extends UiServiceTestCase {
@Mock RankingConfig mConfig;
+ BubbleExtractor mBubbleExtractor;
private String mPkg = "com.android.server.notification";
private int mId = 1001;
@@ -57,6 +59,10 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mBubbleExtractor = new BubbleExtractor();
+ mBubbleExtractor.initialize(mContext, mock(NotificationUsageStats.class));
+ mBubbleExtractor.setConfig(mConfig);
+ mBubbleExtractor.setShortcutHelper(mock(ShortcutHelper.class));
}
private NotificationRecord getNotificationRecord(boolean allow, int importanceHigh) {
@@ -83,70 +89,55 @@
@Test
public void testAppYesChannelNo() {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(true);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertFalse(r.canBubble());
}
@Test
public void testAppNoChannelYes() throws Exception {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(true);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);
NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertFalse(r.canBubble());
}
@Test
public void testAppYesChannelYes() {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(true);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertTrue(r.canBubble());
}
@Test
public void testAppNoChannelNo() {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(true);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);
NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertFalse(r.canBubble());
}
@Test
public void testAppYesChannelYesUserNo() {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(false);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertFalse(r.canBubble());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index dc8d010..010f8ac 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -35,7 +35,6 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
-import android.app.Person;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -191,7 +190,8 @@
tweak.canBubble(),
tweak.visuallyInterruptive(),
tweak.isConversation(),
- tweak.getShortcutInfo()
+ tweak.getShortcutInfo(),
+ tweak.isBubble()
);
assertNotEquals(nru, nru2);
}
@@ -270,7 +270,8 @@
canBubble(i),
visuallyInterruptive(i),
isConversation(i),
- getShortcutInfo(i)
+ getShortcutInfo(i),
+ isBubble(i)
);
rankings[i] = ranking;
}
@@ -389,11 +390,15 @@
"title", 0, "titleResName", "text", 0, "textResName",
"disabledMessage", 0, "disabledMessageResName",
null, null, 0, null, 0, 0,
- 0, "iconResName", "bitmapPath", 0,
+ 0, "iconResName", "bitmapPath", null, 0,
null, null);
return si;
}
+ private boolean isBubble(int index) {
+ return index % 4 == 0;
+ }
+
private void assertActionsEqual(
List<Notification.Action> expecteds, List<Notification.Action> actuals) {
assertEquals(expecteds.size(), actuals.size());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 64d481a..f179840 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -41,8 +41,6 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -204,6 +202,8 @@
private TestableNotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
+ private TestableBubbleChecker mTestableBubbleChecker;
+ private ShortcutHelper mShortcutHelper;
@Mock
private IPackageManager mPackageManager;
@Mock
@@ -286,6 +286,10 @@
super(context, logger, notificationInstanceIdSequence);
}
+ RankingHelper getRankingHelper() {
+ return mRankingHelper;
+ }
+
@Override
protected boolean isCallingUidSystem() {
countSystemChecks++;
@@ -337,6 +341,14 @@
interface NotificationAssistantAccessGrantedCallback {
void onGranted(ComponentName assistant, int userId, boolean granted);
}
+ }
+
+ private class TestableBubbleChecker extends BubbleExtractor.BubbleChecker {
+
+ TestableBubbleChecker(Context context, ShortcutHelper helper, RankingConfig config,
+ ActivityManager manager) {
+ super(context, helper, config, manager);
+ }
@Override
protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
@@ -448,7 +460,16 @@
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mService.setAudioManager(mAudioManager);
- mService.setLauncherApps(mLauncherApps);
+
+ mShortcutHelper = mService.getShortcutHelper();
+ mShortcutHelper.setLauncherApps(mLauncherApps);
+
+ // Set the testable bubble extractor
+ RankingHelper rankingHelper = mService.getRankingHelper();
+ BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class);
+ mTestableBubbleChecker = new TestableBubbleChecker(mContext, mShortcutHelper,
+ mService.mPreferencesHelper, mActivityManager);
+ extractor.setBubbleChecker(mTestableBubbleChecker);
// Tests call directly into the Binder.
mBinderService = mService.getBinderService();
@@ -462,6 +483,15 @@
}
@After
+ public void assertNotificationRecordLoggerCallsValid() {
+ for (NotificationRecordLoggerFake.CallRecord call : mNotificationRecordLogger.getCalls()) {
+ if (call.wasLogged) {
+ assertNotNull(call.event);
+ }
+ }
+ }
+
+ @After
public void tearDown() throws Exception {
if (mFile != null) mFile.delete();
clearDeviceConfig();
@@ -1150,10 +1180,10 @@
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
- assertEquals(1, mNotificationRecordLogger.getCalls().size());
+ assertEquals(1, mNotificationRecordLogger.numCalls());
NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0);
- assertTrue(call.shouldLogReported);
+ assertTrue(call.wasLogged);
assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
call.event);
assertNotNull(call.r);
@@ -1179,18 +1209,18 @@
.setCategory(Notification.CATEGORY_ALARM).build();
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, update, 0);
waitForIdle();
- assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertEquals(2, mNotificationRecordLogger.numCalls());
- assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+ assertTrue(mNotificationRecordLogger.get(0).wasLogged);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(0).event);
+ mNotificationRecordLogger.event(0));
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
- assertTrue(mNotificationRecordLogger.get(1).shouldLogReported);
+ assertTrue(mNotificationRecordLogger.get(1).wasLogged);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED,
- mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(1));
// Instance ID doesn't change on update of an active notification
assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
}
@@ -1203,13 +1233,13 @@
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
- assertEquals(2, mNotificationRecordLogger.getCalls().size());
- assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertTrue(mNotificationRecordLogger.get(0).wasLogged);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(0).event);
- assertFalse(mNotificationRecordLogger.get(1).shouldLogReported);
- assertNull(mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(0));
+ assertFalse(mNotificationRecordLogger.get(1).wasLogged);
+ assertNull(mNotificationRecordLogger.event(1));
}
@Test
@@ -1222,11 +1252,11 @@
notif.extras.putString(Notification.EXTRA_TITLE, "Changed title");
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notif, 0);
waitForIdle();
- assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertEquals(2, mNotificationRecordLogger.numCalls());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(0).event);
- assertNull(mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(0));
+ assertNull(mNotificationRecordLogger.event(1));
}
@Test
@@ -1241,23 +1271,23 @@
waitForIdle();
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notification, 0);
waitForIdle();
- assertEquals(3, mNotificationRecordLogger.getCalls().size());
+ assertEquals(3, mNotificationRecordLogger.numCalls());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(0).event);
- assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+ mNotificationRecordLogger.event(0));
+ assertTrue(mNotificationRecordLogger.get(0).wasLogged);
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL,
- mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(1));
assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(2).event);
- assertTrue(mNotificationRecordLogger.get(2).shouldLogReported);
+ mNotificationRecordLogger.event(2));
+ assertTrue(mNotificationRecordLogger.get(2).wasLogged);
// New instance ID because notification was canceled before re-post
assertEquals(2, mNotificationRecordLogger.get(2).getInstanceId());
}
@@ -1269,7 +1299,7 @@
waitForIdle();
// The notification record logger doesn't even get called when a nonexistent notification
// is cancelled, because that happens very frequently and is not interesting.
- assertEquals(0, mNotificationRecordLogger.getCalls().size());
+ assertEquals(0, mNotificationRecordLogger.numCalls());
}
@Test
@@ -2527,6 +2557,13 @@
// only snooze the one notification
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
assertTrue(nonGrouped.getStats().hasSnoozed());
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+ mNotificationRecordLogger.event(1));
}
@Test
@@ -2569,6 +2606,12 @@
// only snooze the one child
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+ .NOTIFICATION_CANCEL_SNOOZED, mNotificationRecordLogger.event(1));
}
@Test
@@ -2588,6 +2631,18 @@
// snooze child and summary
verify(mSnoozeHelper, times(2)).snooze(any(NotificationRecord.class), anyLong());
+
+ assertEquals(4, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+ mNotificationRecordLogger.event(1));
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(2));
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+ mNotificationRecordLogger.event(3));
}
@Test
@@ -2603,6 +2658,13 @@
// snooze child only
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+ mNotificationRecordLogger.event(1));
}
@Test
@@ -3386,6 +3448,10 @@
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.getSbn()));
+
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
+ mNotificationRecordLogger.event(0));
}
@Test
@@ -3404,6 +3470,12 @@
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
eq((false)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DETAIL_OPEN_USER,
+ mNotificationRecordLogger.event(0));
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DETAIL_CLOSE_USER,
+ mNotificationRecordLogger.event(1));
}
@Test
@@ -3465,11 +3537,11 @@
// Using mService.addNotification() does not generate a NotificationRecordLogger log,
// so we only get the cancel notification.
- assertEquals(1, mNotificationRecordLogger.getCalls().size());
+ assertEquals(1, mNotificationRecordLogger.numCalls());
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD,
- mNotificationRecordLogger.get(0).event);
+ mNotificationRecordLogger.event(0));
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
}
@@ -4351,9 +4423,9 @@
{NotificationVisibility.obtain(r.getKey(), 1, 1, true)},
new NotificationVisibility[]{});
- assertEquals(1, mNotificationRecordLogger.getCalls().size());
+ assertEquals(1, mNotificationRecordLogger.numCalls());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN,
- mNotificationRecordLogger.get(0).event);
+ mNotificationRecordLogger.event(0));
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
mService.mNotificationDelegate.onNotificationVisibilityChanged(
@@ -4362,9 +4434,9 @@
{NotificationVisibility.obtain(r.getKey(), 1, 1, true)}
);
- assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertEquals(2, mNotificationRecordLogger.numCalls());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE,
- mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(1));
assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
}
@@ -4788,6 +4860,12 @@
mService.mNotificationDelegate.onPanelHidden();
verify(mAssistants, times(1)).onPanelHidden();
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_OPEN,
+ mNotificationRecordLogger.event(0));
+ assertEquals(NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_CLOSE,
+ mNotificationRecordLogger.event(1));
}
@Test
@@ -4806,6 +4884,9 @@
modifiedBeforeSending);
verify(mAssistants).notifyAssistantSuggestedReplySent(
eq(r.getSbn()), eq(reply), eq(generatedByAssistant));
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
+ mNotificationRecordLogger.event(0));
}
@Test
@@ -4825,6 +4906,10 @@
generatedByAssistant);
verify(mAssistants).notifyAssistantActionClicked(
eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant));
+
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_ACTION_CLICKED,
+ mNotificationRecordLogger.event(0));
}
@Test
@@ -5489,6 +5574,10 @@
StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifs.length);
assertEquals(1, mService.getNotificationRecordCount());
+
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+ .NOTIFICATION_CANCEL_LISTENER_CANCEL, mNotificationRecordLogger.event(0));
}
@Test
@@ -5710,6 +5799,9 @@
@Test
public void testOnBubbleNotificationSuppressionChanged() throws Exception {
+ // Bubbles are allowed!
+ setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+
// Bubble notification
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag");
@@ -6111,8 +6203,10 @@
assertTrue(notif.isBubbleNotification());
// Test: Remove the shortcut
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
launcherAppsCallback.getValue().onShortcutsChanged(PKG, Collections.emptyList(),
new UserHandle(mUid));
+ waitForIdle();
// Verify:
@@ -6125,6 +6219,58 @@
assertFalse(notif2.isBubbleNotification());
}
+
+ @Test
+ public void testNotificationBubbles_shortcut_stopListeningWhenNotifRemoved()
+ throws RemoteException {
+ // Bubbles are allowed!
+ setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+
+ ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback =
+ ArgumentCaptor.forClass(LauncherApps.Callback.class);
+
+ // Messaging notification with shortcut info
+ Notification.BubbleMetadata metadata =
+ getBubbleMetadataBuilder().createShortcutBubble("someshortcutId").build();
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+ null /* groupKey */, false /* isSummary */);
+ nb.setBubbleMetadata(metadata);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "tag", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ // Pretend the shortcut exists
+ List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.isLongLived()).thenReturn(true);
+ shortcutInfos.add(info);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+
+ // Test: Send the bubble notification
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // Verify:
+
+ // Make sure we register the callback for shortcut changes
+ verify(mLauncherApps, times(1)).registerCallback(launcherAppsCallback.capture(), any());
+
+ // yes allowed, yes messaging w/shortcut, yes bubble
+ Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
+ assertTrue(notif.isBubbleNotification());
+
+ // Test: Remove the notification
+ mBinderService.cancelNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // Verify:
+
+ // Make sure callback is unregistered
+ verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
+ }
+
@Test
public void testNotificationBubbles_bubbleChildrenStay_whenGroupSummaryDismissed()
throws Exception {
@@ -6168,6 +6314,17 @@
// The bubble should still exist
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
+
+ // Check we got the click log and associated dismissal logs
+ assertEquals(6, mNotificationRecordLogger.numCalls());
+ // Skip the notification-creation logs
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED,
+ mNotificationRecordLogger.event(3));
+ assertEquals(NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_CLICK,
+ mNotificationRecordLogger.event(4));
+ assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+ .NOTIFICATION_CANCEL_GROUP_SUMMARY_CANCELED,
+ mNotificationRecordLogger.event(5));
}
@Test
@@ -6188,6 +6345,11 @@
// THEN the bubble should still exist
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
+
+ // Check we got the click log
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED,
+ mNotificationRecordLogger.event(0));
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
index 2a17bae..6b18cc6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -31,25 +31,29 @@
// The following fields are only relevant to maybeLogNotificationPosted() calls.
static final int INVALID = -1;
public int position = INVALID, buzzBeepBlink = INVALID;
- public boolean shouldLogReported;
+ public boolean wasLogged;
CallRecord(NotificationRecord r, NotificationRecord old, int position,
int buzzBeepBlink) {
super(r, old);
this.position = position;
this.buzzBeepBlink = buzzBeepBlink;
- shouldLogReported = shouldLogReported(buzzBeepBlink);
- event = shouldLogReported ? NotificationReportedEvent.fromRecordPair(this) : null;
+ wasLogged = shouldLogReported(buzzBeepBlink);
+ event = wasLogged ? NotificationReportedEvent.fromRecordPair(this) : null;
}
CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) {
super(r, null);
- shouldLogReported = false;
+ wasLogged = true;
this.event = event;
}
}
private List<CallRecord> mCalls = new ArrayList<>();
+ public int numCalls() {
+ return mCalls.size();
+ }
+
List<CallRecord> getCalls() {
return mCalls;
}
@@ -57,6 +61,9 @@
CallRecord get(int index) {
return mCalls.get(index);
}
+ UiEventLogger.UiEventEnum event(int index) {
+ return mCalls.get(index).event;
+ }
@Override
public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old,
@@ -65,13 +72,12 @@
}
@Override
- public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
- mCalls.add(new CallRecord(r,
- NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface)));
+ public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
+ mCalls.add(new CallRecord(r, event));
}
@Override
- public void logNotificationVisibility(NotificationRecord r, boolean visible) {
- mCalls.add(new CallRecord(r, NotificationEvent.fromVisibility(visible)));
+ public void log(UiEventLogger.UiEventEnum event) {
+ mCalls.add(new CallRecord(null, event));
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
new file mode 100644
index 0000000..50fb9b4
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.notification;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper
+public class ShortcutHelperTest extends UiServiceTestCase {
+
+ private static final String SHORTCUT_ID = "shortcut";
+ private static final String PKG = "pkg";
+ private static final String KEY = "key";
+
+ @Mock
+ LauncherApps mLauncherApps;
+ @Mock
+ ShortcutHelper.ShortcutListener mShortcutListener;
+ @Mock
+ NotificationRecord mNr;
+ @Mock
+ Notification mNotif;
+ @Mock
+ StatusBarNotification mSbn;
+ @Mock
+ Notification.BubbleMetadata mBubbleMetadata;
+
+ ShortcutHelper mShortcutHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mShortcutHelper = new ShortcutHelper(mLauncherApps, mShortcutListener);
+ when(mNr.getKey()).thenReturn(KEY);
+ when(mNr.getSbn()).thenReturn(mSbn);
+ when(mSbn.getPackageName()).thenReturn(PKG);
+ when(mNr.getNotification()).thenReturn(mNotif);
+ when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata);
+ when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
+ }
+
+ private LauncherApps.Callback addShortcutBubbleAndVerifyListener() {
+ when(mNotif.isBubbleNotification()).thenReturn(true);
+
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+ false /* removed */,
+ null /* handler */);
+
+ ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback =
+ ArgumentCaptor.forClass(LauncherApps.Callback.class);
+
+ verify(mLauncherApps, times(1)).registerCallback(
+ launcherAppsCallback.capture(), any());
+ return launcherAppsCallback.getValue();
+ }
+
+ @Test
+ public void testBubbleAdded_listenedAdded() {
+ addShortcutBubbleAndVerifyListener();
+ }
+
+ @Test
+ public void testBubbleRemoved_listenerRemoved() {
+ // First set it up to listen
+ addShortcutBubbleAndVerifyListener();
+
+ // Then remove the notif
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+ true /* removed */,
+ null /* handler */);
+
+ verify(mLauncherApps, times(1)).unregisterCallback(any());
+ }
+
+ @Test
+ public void testBubbleNoLongerBubble_listenerRemoved() {
+ // First set it up to listen
+ addShortcutBubbleAndVerifyListener();
+
+ // Then make it not a bubble
+ when(mNotif.isBubbleNotification()).thenReturn(false);
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+ false /* removed */,
+ null /* handler */);
+
+ verify(mLauncherApps, times(1)).unregisterCallback(any());
+ }
+
+ @Test
+ public void testListenerNotifiedOnShortcutRemoved() {
+ LauncherApps.Callback callback = addShortcutBubbleAndVerifyListener();
+
+ List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+
+ callback.onShortcutsChanged(PKG, shortcutInfos, mock(UserHandle.class));
+ verify(mShortcutListener).onShortcutRemoved(mNr.getKey());
+ }
+}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cdba9a1..6cb8b86 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -20,6 +20,7 @@
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"servicestests-utils",
+ "testng",
"truth-prebuilt",
"testables",
"ub-uiautomator",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index c9c3649..9e87421 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1332,6 +1332,40 @@
assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
}
+ @Test
+ public void testActivityDestroyDoesntChangeProcessOverride() {
+ final ActivityRecord firstActivity =
+ createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
+ final WindowProcessController wpc = firstActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, firstActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ final ActivityRecord secondActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, wpc);
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ final ActivityRecord thirdActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, wpc);
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ secondActivity.destroyImmediately(true, "");
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ firstActivity.destroyImmediately(true, "");
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
/**
* Creates an activity on display. For non-default display request it will also create a new
* display with custom DisplayInfo.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index af04f76..fc256b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -246,7 +246,7 @@
}
@Test
- public void testWorkChallenge() {
+ public void testLockedManagedProfile() {
// GIVEN that the user the activity is starting as is currently locked
when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index e2c27ea..7928e76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -20,6 +20,9 @@
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -42,13 +45,17 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
+import static org.testng.Assert.expectThrows;
import android.app.WindowConfiguration;
import android.graphics.Insets;
@@ -154,6 +161,56 @@
}
@Test
+ public void addingWindow_withInsetsTypes() {
+ WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+ win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
+ win.getFrameLw().set(0, 0, 500, 100);
+
+ addWindow(win);
+ InsetsStateController controller = mDisplayContent.getInsetsStateController();
+ controller.onPostLayout();
+
+ InsetsSourceProvider statusBarProvider = controller.getSourceProvider(ITYPE_STATUS_BAR);
+ assertEquals(new Rect(0, 0, 500, 100), statusBarProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 100, 0, 0),
+ statusBarProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+
+ InsetsSourceProvider topGesturesProvider = controller.getSourceProvider(ITYPE_TOP_GESTURES);
+ assertEquals(new Rect(0, 0, 500, 100), topGesturesProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 100, 0, 0),
+ topGesturesProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+
+ InsetsSourceProvider navigationBarProvider = controller.getSourceProvider(
+ ITYPE_NAVIGATION_BAR);
+ assertNotEquals(new Rect(0, 0, 500, 100), navigationBarProvider.getSource().getFrame());
+ }
+
+ @Test
+ public void addingWindow_ignoresInsetsTypes_InWindowTypeWithPredefinedInsets() {
+ mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
+ WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
+ win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
+ win.getFrameLw().set(0, 0, 500, 100);
+
+ addWindow(win);
+ mDisplayContent.getInsetsStateController().onPostLayout();
+
+ InsetsSourceProvider provider =
+ mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR);
+ assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+ }
+
+ @Test
+ public void addingWindow_throwsException_WithMultipleInsetTypes() {
+ WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+ win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
+
+ expectThrows(IllegalArgumentException.class, () -> addWindow(win));
+ }
+
+ @Test
public void layoutWindowLw_fitStatusBars() {
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 5cf1fbb..a380ece 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -69,7 +71,7 @@
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
- assertNotNull(getController().getInsetsForDispatch(app).getSource(ITYPE_STATUS_BAR));
+ assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
}
@Test
@@ -101,6 +103,34 @@
}
@Test
+ public void testStripForDispatch_pip() {
+ final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ app.setWindowingMode(WINDOWING_MODE_PINNED);
+
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+ }
+
+ @Test
+ public void testStripForDispatch_freeform() {
+ final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ app.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+ }
+
+ @Test
public void testImeForDispatch() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 12d89de..8f3ff52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -37,8 +37,8 @@
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.LocalServices;
import com.android.server.pm.PackageList;
@@ -72,6 +72,10 @@
ComponentName.createRelative("com.android.foo", ".BarActivity");
private static final ComponentName ALTERNATIVE_COMPONENT =
ComponentName.createRelative("com.android.foo", ".AlternativeBarActivity");
+ private static final String TEST_WINDOW_LAYOUT_AFFINITY = "135:.Affinity";
+ private static final String TEST_ALTERNATIVE_WINDOW_LAYOUT_AFFINITY =
+ "246:.AlternativeAffinity";
+ private static final String TEST_DIFFERENT_AFFINITY_WITH_SAME_UID = "135:.DifferentAffinity";
private static final int TEST_WINDOWING_MODE = WINDOWING_MODE_FREEFORM;
private static final Rect TEST_BOUNDS = new Rect(100, 200, 300, 400);
@@ -99,11 +103,12 @@
public void setUp() throws Exception {
mPersisterQueue = new TestPersisterQueue();
- final File cacheFolder = InstrumentationRegistry.getContext().getCacheDir();
+ final File cacheFolder =
+ InstrumentationRegistry.getInstrumentation().getContext().getCacheDir();
mFolder = new File(cacheFolder, "launch_params_tests");
deleteRecursively(mFolder);
- mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++);
+ mDisplayUniqueId = "test:" + sNextUniqueId++;
mTestDisplay = new TestDisplayContent.Builder(mService, 1000, 1500)
.setUniqueId(mDisplayUniqueId).build();
when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId)))
@@ -211,6 +216,61 @@
}
@Test
+ public void testUsesRecordWithSameWindowLayoutAffinityInSameInstance_NoPreviousRecord() {
+ mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.saveTask(mTestTask);
+
+ mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+ assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
+ assertEquals(TEST_BOUNDS, mResult.mBounds);
+ }
+
+ @Test
+ public void testUsesRecordWithSameWindowLayoutAffinityInSameInstance_HasOldPreviousRecord()
+ throws Exception {
+ mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.saveTask(mTaskWithDifferentComponent);
+
+ Thread.sleep(1); // Sleep 1ms so that the timestamp can for sure increase.
+
+ mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.saveTask(mTestTask);
+
+ mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+ assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
+ assertEquals(TEST_BOUNDS, mResult.mBounds);
+ }
+
+ @Test
+ public void testReturnsEmptyLaunchParamsUidInLaunchAffinityMismatch() {
+ mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.saveTask(mTestTask);
+
+ mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_DIFFERENT_AFFINITY_WITH_SAME_UID;
+ mResult.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+ assertTrue("Result must be empty.", mResult.isEmpty());
+ }
+
+ @Test
+ public void testReturnsEmptyLaunchParamsWindowLayoutAffinityMismatch() {
+ mTestTask.affinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.saveTask(mTestTask);
+
+ mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_ALTERNATIVE_WINDOW_LAYOUT_AFFINITY;
+ mResult.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ mTarget.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+ assertTrue("Result must be empty.", mResult.isEmpty());
+ }
+
+ @Test
public void testSavesAndRestoresLaunchParamsAcrossInstances() {
mTarget.saveTask(mTestTask);
mPersisterQueue.flush();
@@ -228,6 +288,52 @@
}
@Test
+ public void testUsesRecordWithSameWindowLayoutAffinityAcrossInstances_NoPreviousRecord() {
+ mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.saveTask(mTestTask);
+ mPersisterQueue.flush();
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+
+ mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ target.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+ assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
+ assertEquals(TEST_BOUNDS, mResult.mBounds);
+ }
+
+ @Test
+ public void testUsesRecordWithSameWindowLayoutAffinityAcrossInstances_HasOldPreviousRecord()
+ throws Exception {
+ mTaskWithDifferentComponent.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.saveTask(mTaskWithDifferentComponent);
+ mPersisterQueue.flush();
+
+ // Sleep 1s because many file systems only save last modified time as precise as 1s so we
+ // can for sure know the last modified time is different.
+ Thread.sleep(1000);
+
+ mTestTask.mWindowLayoutAffinity = TEST_WINDOW_LAYOUT_AFFINITY;
+ mTarget.saveTask(mTestTask);
+ mPersisterQueue.flush();
+
+ final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+ mUserFolderGetter);
+ target.onSystemReady();
+ target.onUnlockUser(TEST_USER_ID);
+
+ target.getLaunchParams(mTaskWithDifferentComponent, null, mResult);
+
+ assertEquals(mTestDisplay.mDisplayId, mResult.mPreferredDisplayId);
+ assertEquals(TEST_WINDOWING_MODE, mResult.mWindowingMode);
+ assertEquals(TEST_BOUNDS, mResult.mBounds);
+ }
+
+ @Test
public void testClearsRecordsOfTheUserOnUserCleanUp() {
mTarget.saveTask(mTestTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 277bc41..67b1dac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -27,6 +27,8 @@
import static org.junit.Assert.assertTrue;
import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -109,5 +111,27 @@
assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
mWm.getDefaultDisplayContentLocked().getWindowingMode());
}
+
+ /**
+ * This test ensures that an existing single instance activity with alias name can be found by
+ * the same activity info. So {@link ActivityStarter#getReusableTask} won't miss it that leads
+ * to create an unexpected new instance.
+ */
+ @Test
+ public void testFindActivityByTargetComponent() {
+ final ComponentName aliasComponent = ComponentName.createRelative(
+ ActivityTestsBase.DEFAULT_COMPONENT_PACKAGE_NAME, ".AliasActivity");
+ final ComponentName targetComponent = ComponentName.createRelative(
+ aliasComponent.getPackageName(), ".TargetActivity");
+ final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setComponent(aliasComponent)
+ .setTargetActivity(targetComponent.getClassName())
+ .setLaunchMode(ActivityInfo.LAUNCH_SINGLE_INSTANCE)
+ .setCreateTask(true)
+ .build();
+
+ assertEquals(activity, mWm.mRoot.findActivity(activity.intent, activity.info,
+ false /* compareIntentFilters */));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index ba4a82f..edf81ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -107,10 +107,16 @@
setUpApp(display);
// Put app window into freeform and then make it a compat app.
- mTask.setBounds(100, 100, 400, 600);
+ final Rect bounds = new Rect(100, 100, 400, 600);
+ mTask.setBounds(bounds);
prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ assertEquals(bounds, mActivity.getBounds());
- final Rect bounds = new Rect(mActivity.getBounds());
+ // The activity should be able to accept negative x position [-150, 100 - 150, 600].
+ final int dx = bounds.left + bounds.width() / 2;
+ mTask.setBounds(bounds.left - dx, bounds.top, bounds.right - dx, bounds.bottom);
+ assertEquals(mTask.getBounds(), mActivity.getBounds());
+
final int density = mActivity.getConfiguration().densityDpi;
// change display configuration to fullscreen
@@ -231,11 +237,7 @@
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
- final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
- mService.mWindowManager, mock(Session.class), new TestIWindow(),
- new WindowManager.LayoutParams(), mActivity);
- mActivity.addWindow(w);
- mActivity.mDisplayContent.mInputMethodTarget = w;
+ mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity);
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
}
@@ -475,6 +477,25 @@
assertEquals((int) (dw * maxAspect), mActivity.getBounds().width());
// The bounds should be horizontal centered: (2500-1900)/2=350.
assertEquals((dh - mActivity.getBounds().width()) / 2, mActivity.getBounds().left);
+
+ // The letterbox needs a main window to layout.
+ addWindowToActivity(mActivity);
+ // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
+ mActivity.mRootWindowContainer.performSurfacePlacement(false /* recoveringMemory */);
+ // The letterbox insets should be [350, 0 - 350, 0].
+ assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
+ mActivity.getLetterboxInsets());
+ }
+
+ private WindowState addWindowToActivity(ActivityRecord activity) {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+ final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
+ mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
+ WindowTestsBase.makeWindowVisible(w);
+ w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ mActivity.addWindow(w);
+ return w;
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 560d03f..091f493 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -322,6 +322,8 @@
}
private void tearDown() {
+ mWmService.mRoot.forAllDisplayPolicies(DisplayPolicy::release);
+
// Unregister display listener from root to avoid issues with subsequent tests.
mContext.getSystemService(DisplayManager.class)
.unregisterDisplayListener(mAtmService.mRootWindowContainer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 53a3682..71c837e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -32,6 +32,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
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;
@@ -213,6 +214,16 @@
}
@Test
+ public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ stack.setWindowingMode(WINDOWING_MODE_PINNED);
+
+ final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
+ verify(organizer, times(1)).taskAppeared(any());
+ }
+
+ @Test
public void testTaskTransaction() {
removeGlobalMinSizeRestriction();
final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
@@ -241,7 +252,33 @@
}
@Test
- public void testContainerChanges() {
+ public void testSetWindowingMode() {
+ final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ final WindowContainerTransaction t = new WindowContainerTransaction();
+
+ t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+
+ assertEquals(WINDOWING_MODE_FULLSCREEN, stack.getWindowingMode());
+ }
+
+ @Test
+ public void testSetActivityWindowingMode() {
+ final ActivityRecord record = makePipableActivity();
+ final ActivityStack stack = record.getStack();
+ final WindowContainerTransaction t = new WindowContainerTransaction();
+
+ t.setWindowingMode(stack.mRemoteToken, WINDOWING_MODE_PINNED);
+ t.setActivityWindowingMode(stack.mRemoteToken, WINDOWING_MODE_FULLSCREEN);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+
+ assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode());
+ assertEquals(WINDOWING_MODE_PINNED, stack.getWindowingMode());
+ }
+
+ @Test
+ public void testContainerFocusableChanges() {
removeGlobalMinSizeRestriction();
final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
@@ -251,6 +288,24 @@
t.setFocusable(stack.mRemoteToken, false);
mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
assertFalse(task.isFocusable());
+ t.setFocusable(stack.mRemoteToken, true);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ assertTrue(task.isFocusable());
+ }
+
+ @Test
+ public void testContainerHiddenChanges() {
+ removeGlobalMinSizeRestriction();
+ final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ assertTrue(stack.shouldBeVisible(null));
+ t.setHidden(stack.mRemoteToken, true);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ assertFalse(stack.shouldBeVisible(null));
+ t.setHidden(stack.mRemoteToken, false);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ assertTrue(stack.shouldBeVisible(null));
}
@Test
@@ -495,6 +550,9 @@
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ITaskOrganizer organizer = registerMockOrganizer();
+ spyOn(task);
+ doReturn(true).when(task).isVisible();
+
BLASTSyncEngine bse = new BLASTSyncEngine();
BLASTSyncEngine.TransactionReadyListener transactionListener =
@@ -509,11 +567,41 @@
}
@Test
+ public void testOverlappingBLASTCallback() throws RemoteException {
+ final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final ITaskOrganizer organizer = registerMockOrganizer();
+
+ spyOn(task);
+ doReturn(true).when(task).isVisible();
+ final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+ makeWindowVisible(w);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine();
+
+ BLASTSyncEngine.TransactionReadyListener transactionListener =
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(transactionListener);
+ assertEquals(true, bse.addToSyncSet(id, task));
+ bse.setReady(id);
+
+ int id2 = bse.startSyncSet(transactionListener);
+ // We should be rejected from the second sync since we are already
+ // in one.
+ assertEquals(false, bse.addToSyncSet(id2, task));
+ w.finishDrawing(null);
+ assertEquals(true, bse.addToSyncSet(id2, task));
+ bse.setReady(id2);
+ }
+
+ @Test
public void testBLASTCallbackWithWindow() {
final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+ makeWindowVisible(w);
BLASTSyncEngine bse = new BLASTSyncEngine();
@@ -531,6 +619,60 @@
.transactionReady(anyInt(), any());
}
+ @Test
+ public void testBLASTCallbackWithInvisibleWindow() {
+ final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+
+ BLASTSyncEngine bse = new BLASTSyncEngine();
+
+ BLASTSyncEngine.TransactionReadyListener transactionListener =
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(transactionListener);
+ bse.addToSyncSet(id, task);
+ bse.setReady(id);
+
+ // Since the window was invisible, the Task had no visible leaves and the sync should
+ // complete as soon as we call setReady.
+ verify(transactionListener)
+ .transactionReady(anyInt(), any());
+ }
+
+ @Test
+ public void testBLASTCallbackWithChildWindow() {
+ final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+ final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window");
+
+ w.mActivityRecord.setVisible(true);
+ makeWindowVisible(w, child);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine();
+
+ BLASTSyncEngine.TransactionReadyListener transactionListener =
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(transactionListener);
+ assertEquals(true, bse.addToSyncSet(id, task));
+ bse.setReady(id);
+ w.finishDrawing(null);
+
+ // Since we have a child window we still shouldn't be done.
+ verify(transactionListener, never())
+ .transactionReady(anyInt(), any());
+ reset(transactionListener);
+
+ child.finishDrawing(null);
+ // Ah finally! Done
+ verify(transactionListener)
+ .transactionReady(anyInt(), any());
+ }
+
class StubOrganizer extends ITaskOrganizer.Stub {
RunningTaskInfo mInfo;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index 2fb0b4c..8f913dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -81,9 +81,9 @@
assertEquals(TEST_INSETS, snapshot.getContentInsets());
assertNotNull(snapshot.getSnapshot());
assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
+ assertNull(mLoader.loadTask(1, mTestUserId, true /* isLowResolution */));
}
-
@Test
public void testRemoveObsoleteFiles() {
mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
@@ -132,10 +132,18 @@
assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
false /* restoreFromDisk */, false /* isLowResolution */));
- // Load it from disk
+ // Attempt to load the low-res snapshot from the disk
assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
true /* restoreFromDisk */, true /* isLowResolution */));
+ // Load the high-res (default) snapshot from disk
+ assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
+ true /* restoreFromDisk */, false /* isLowResolution */));
+
+ // Make sure it's not in the cache now.
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
+ false /* restoreFromDisk */, true /* isLowResolution */));
+
// Make sure it's not in the cache now.
assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
false /* restoreFromDisk */, false /* isLowResolution */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 9a9abba..bfee894 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -293,35 +293,6 @@
}
@Test
- public void testDisabledLowResolutionPersistAndLoadSnapshot() {
- mPersister.setEnableLowResSnapshots(false);
-
- TaskSnapshot a = new TaskSnapshotBuilder()
- .setScaleFraction(0.5f)
- .setIsLowResolution(true)
- .build();
- assertTrue(a.isLowResolution());
- mPersister.persistSnapshot(1, mTestUserId, a);
- mPersister.waitForQueueEmpty();
- final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
- final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
- };
- assertTrueForFiles(files, File::exists, " must exist");
- assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
- final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
- assertNotNull(snapshot);
- assertEquals(TEST_INSETS, snapshot.getContentInsets());
- assertNotNull(snapshot.getSnapshot());
- assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
-
- final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId,
- true /* isLowResolution */);
- assertNull(snapshotNotExist);
- }
-
- @Test
public void testIsRealSnapshotPersistAndLoadSnapshot() {
TaskSnapshot a = new TaskSnapshotBuilder()
.setIsRealSnapshot(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 6d78658..88de34d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -127,7 +127,6 @@
private static final int SNAPSHOT_HEIGHT = 100;
private float mScaleFraction = 1f;
- private boolean mIsLowResolution = false;
private boolean mIsRealSnapshot = true;
private boolean mIsTranslucent = false;
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -142,11 +141,6 @@
return this;
}
- TaskSnapshotBuilder setIsLowResolution(boolean isLowResolution) {
- mIsLowResolution = isLowResolution;
- return this;
- }
-
TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
mIsRealSnapshot = isRealSnapshot;
return this;
@@ -186,8 +180,11 @@
return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
mRotation, taskSize, TEST_INSETS,
- mIsLowResolution, mIsRealSnapshot,
- mWindowingMode, mSystemUiVisibility, mIsTranslucent);
+ // When building a TaskSnapshot with the Builder class, isLowResolution
+ // is always false. Low-res snapshots are only created when loading from
+ // disk.
+ false /* isLowResolution */,
+ mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index e1475a4..91c3c27 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -77,7 +77,8 @@
}
@Override
- public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync)
+ public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom,
+ boolean sync)
throws RemoteException {
}
@@ -85,7 +86,6 @@
public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras,
boolean sync) throws RemoteException {
}
-
@Override
public void dispatchDragEvent(DragEvent event) throws RemoteException {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index aa66524..900f014 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -17,19 +17,29 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Gravity;
@@ -139,4 +149,124 @@
assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation);
assertEquals(portraitFrame, wallpaperWindow.getFrameLw());
}
+
+ @Test
+ public void testWallpaperZoom() throws RemoteException {
+ final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */);
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+ wallpaperWindow.getAttrs().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
+
+ final WindowState homeWindow = createWallpaperTargetWindow(dc);
+
+ spyOn(dc.mWallpaperController);
+ doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
+
+ dc.mWallpaperController.adjustWallpaperWindows();
+
+ spyOn(wallpaperWindow.mClient);
+
+ float zoom = .5f;
+ dc.mWallpaperController.setWallpaperZoomOut(homeWindow, zoom);
+ assertEquals(zoom, wallpaperWindow.mWallpaperZoomOut, .01f);
+ verify(wallpaperWindow.mClient).dispatchWallpaperOffsets(anyFloat(), anyFloat(), anyFloat(),
+ anyFloat(), eq(zoom), anyBoolean());
+ }
+
+ @Test
+ public void testWallpaperZoom_shouldNotScaleWallpaper() throws RemoteException {
+ final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */);
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+ wallpaperWindow.getAttrs().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
+
+ final WindowState homeWindow = createWallpaperTargetWindow(dc);
+
+ spyOn(dc.mWallpaperController);
+ doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
+
+ dc.mWallpaperController.adjustWallpaperWindows();
+
+ spyOn(wallpaperWindow.mClient);
+
+ float newZoom = .5f;
+ wallpaperWindow.mShouldScaleWallpaper = false;
+ // Set zoom, and make sure the window animator scale didn't actually change, but the zoom
+ // value did, and we do dispatch the zoom to the wallpaper service
+ dc.mWallpaperController.setWallpaperZoomOut(homeWindow, newZoom);
+ assertEquals(newZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
+ assertEquals(1f, wallpaperWindow.mWinAnimator.mWallpaperScale, .01f);
+ verify(wallpaperWindow.mClient).dispatchWallpaperOffsets(anyFloat(), anyFloat(), anyFloat(),
+ anyFloat(), eq(newZoom), anyBoolean());
+ }
+
+ @Test
+ public void testWallpaperZoom_multipleCallers() {
+ final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, dc,
+ true /* ownerCanManageAppTokens */);
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+ wallpaperWindow.getAttrs().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
+
+
+ spyOn(dc.mWallpaperController);
+ doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
+
+ final WindowState homeWindow = createWallpaperTargetWindow(dc);
+
+ WindowState otherWindow = createWindow(null /* parent */, TYPE_APPLICATION, dc,
+ "otherWindow");
+
+ dc.mWallpaperController.adjustWallpaperWindows();
+
+ spyOn(wallpaperWindow.mClient);
+
+ // Set zoom from 2 windows
+ float homeWindowInitialZoom = .5f;
+ float otherWindowInitialZoom = .7f;
+ dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowInitialZoom);
+ dc.mWallpaperController.setWallpaperZoomOut(otherWindow, otherWindowInitialZoom);
+ // Make sure the largest one wins
+ assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
+
+ // Change zoom to a larger zoom from homeWindow
+ float homeWindowZoom2 = .8f;
+ dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowZoom2);
+ // New zoom should be current
+ assertEquals(homeWindowZoom2, wallpaperWindow.mWallpaperZoomOut, .01f);
+
+ // Set homeWindow zoom to a lower zoom, but keep the one from otherWindow
+ dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowInitialZoom);
+
+ // Zoom from otherWindow should be the current.
+ assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
+ }
+
+
+ private WindowState createWallpaperTargetWindow(DisplayContent dc) {
+ final ActivityRecord homeActivity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(dc.getRootHomeTask())
+ .setCreateTask(true)
+ .build();
+ homeActivity.setVisibility(true);
+
+ WindowState appWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
+ homeActivity, "wallpaperTargetWindow");
+ appWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+ appWindow.mHasSurface = true;
+ spyOn(appWindow);
+ doReturn(true).when(appWindow).isDrawFinishedLw();
+
+ homeActivity.addWindow(appWindow);
+ return appWindow;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index ce6efdf..926bd8c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -24,6 +24,9 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.content.ContentResolver;
import android.net.Uri;
@@ -60,11 +63,35 @@
public void testFreeformWindow() {
try (SettingsSession freeformWindowSession = new
SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) {
- final boolean freeformWindow = !freeformWindowSession.getSetting();
- final Uri freeformWindowUri = freeformWindowSession.setSetting(freeformWindow);
+ final boolean curFreeformWindow = freeformWindowSession.getSetting();
+ final boolean newFreeformWindow = !curFreeformWindow;
+ final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = curFreeformWindow;
mWm.mSettingsObserver.onChange(false, freeformWindowUri);
- assertEquals(mWm.mAtmService.mSupportsFreeformWindowManagement, freeformWindow);
+ assertEquals(mWm.mAtmService.mSupportsFreeformWindowManagement, newFreeformWindow);
+ }
+ }
+
+ @Test
+ public void testFreeformWindow_valueChanged_updatesDisplaySettings() {
+ try (SettingsSession freeformWindowSession = new
+ SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) {
+ final boolean curFreeformWindow = freeformWindowSession.getSetting();
+ final boolean newFreeformWindow = !curFreeformWindow;
+ final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = curFreeformWindow;
+ clearInvocations(mWm.mRoot);
+ mWm.mSettingsObserver.onChange(false, freeformWindowUri);
+
+ // Changed value should update display settings.
+ verify(mWm.mRoot).onSettingsRetrieved();
+
+ clearInvocations(mWm.mRoot);
+ mWm.mSettingsObserver.onChange(false, freeformWindowUri);
+
+ // Unchanged value should not update display settings.
+ verify(mWm.mRoot, never()).onSettingsRetrieved();
}
}
@@ -73,7 +100,8 @@
try (SettingsSession forceResizableSession = new
SettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) {
final boolean forceResizableMode = !forceResizableSession.getSetting();
- final Uri forceResizableUri = forceResizableSession.setSetting(forceResizableMode);
+ final Uri forceResizableUri = forceResizableSession.setSetting(forceResizableMode);
+
mWm.mSettingsObserver.onChange(false, forceResizableUri);
assertEquals(mWm.mAtmService.mForceResizableActivities, forceResizableMode);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index ef9a73b..9d48955 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -98,8 +98,7 @@
// Persist versioned backup files.
// Should be false, except when testing new versions
- // STOPSHIP: b/139937606 this should be false on launch
- static final boolean KEEP_BACKUP_DIR = true;
+ static final boolean KEEP_BACKUP_DIR = false;
private static final String TAG = "UsageStatsDatabase";
private static final boolean DEBUG = UsageStatsService.DEBUG;
@@ -412,6 +411,7 @@
}
if (mBackupsDir.exists() && !KEEP_BACKUP_DIR) {
+ mUpgradePerformed = true; // updated here to ensure that data is cleaned up
deleteDirectory(mBackupsDir);
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0d1b352..bbe9851 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -137,8 +137,7 @@
private static final File USAGE_STATS_LEGACY_DIR = new File(
Environment.getDataSystemDirectory(), "usagestats");
// For migration purposes, indicates whether to keep the legacy usage stats directory or not
- // STOPSHIP: b/138323140 this should be false on launch
- private static final boolean KEEP_LEGACY_DIR = true;
+ private static final boolean KEEP_LEGACY_DIR = false;
private static final char TOKEN_DELIMITER = '/';
@@ -648,7 +647,7 @@
private void deleteLegacyDir(int userId) {
final File legacyUserDir = new File(USAGE_STATS_LEGACY_DIR, Integer.toString(userId));
- if (!KEEP_LEGACY_DIR) {
+ if (!KEEP_LEGACY_DIR && legacyUserDir.exists()) {
deleteRecursively(legacyUserDir);
if (legacyUserDir.exists()) {
Slog.w(TAG, "Error occurred while attempting to delete legacy usage stats "
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 8e85bb2..747c8d9 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -445,7 +445,6 @@
abstract static class UsbHandler extends Handler {
// current USB state
- private boolean mConnected;
private boolean mHostConnected;
private boolean mSourcePower;
private boolean mSinkPower;
@@ -473,6 +472,7 @@
private final UsbPermissionManager mPermissionManager;
private NotificationManager mNotificationManager;
+ protected boolean mConnected;
protected long mScreenUnlockedFunctions;
protected boolean mBootCompleted;
protected boolean mCurrentFunctionsApplied;
@@ -1794,7 +1794,8 @@
case MSG_SET_FUNCTIONS_TIMEOUT:
Slog.e(TAG, "Set functions timed out! no reply from usb hal");
if (msg.arg1 != 1) {
- setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
+ // Set this since default function may be selected from Developer options
+ setEnabledFunctions(mScreenUnlockedFunctions, false);
}
break;
case MSG_GET_CURRENT_USB_FUNCTIONS:
@@ -1816,7 +1817,8 @@
* Dont force to default when the configuration is already set to default.
*/
if (msg.arg1 != 1) {
- setEnabledFunctions(UsbManager.FUNCTION_NONE, !isAdbEnabled());
+ // Set this since default function may be selected from Developer options
+ setEnabledFunctions(mScreenUnlockedFunctions, false);
}
break;
case MSG_GADGET_HAL_REGISTERED:
@@ -1936,8 +1938,11 @@
SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS);
sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions,
SET_FUNCTIONS_TIMEOUT_MS);
- sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
- SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+ if (mConnected) {
+ // Only queue timeout of enumeration when the USB is connected
+ sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
+ SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+ }
if (DEBUG) Slog.d(TAG, "timeout message queued");
} catch (RemoteException e) {
Slog.e(TAG, "Remoteexception while calling setCurrentUsbFunctions", e);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 767010a..3196758 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
@@ -1133,7 +1134,8 @@
}
mIsBound = mContext.bindServiceAsUser(i, this,
- BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE, mUser);
+ BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES,
+ mUser);
if (mIsBound) {
mRemoteServiceWakeLock.acquire();
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index 36c6377..c832f53 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -38,16 +38,14 @@
*
* <p>
* Below is an example manifest registration for a {@code CallRedirectionService}.
- * <pre>
* {@code
* <service android:name="your.package.YourCallRedirectionServiceImplementation"
- * android:permission="android.permission.BIND_REDIRECTION_SERVICE">
+ * android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
* <intent-filter>
* <action android:name="android.telecom.CallRedirectionService"/>
* </intent-filter>
* </service>
* }
- * </pre>
*/
public abstract class CallRedirectionService extends Service {
/**
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 0093843..bebbbd0 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -80,17 +80,20 @@
* Reason code (returned via {@link #getReason()}) which indicates that a call could not be
* completed because the cellular radio is off or out of service, the device is connected to
* a wifi network, but the user has not enabled wifi calling.
+ * @hide
*/
public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
/**
* Reason code (returned via {@link #getReason()}), which indicates that the video telephony
* call was disconnected because IMS access is blocked.
+ * @hide
*/
public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
/**
* Reason code, which indicates that the conference call is simulating single party conference.
+ * @hide
*/
public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 4e6e1a5..768c8ee 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -53,7 +53,6 @@
* {@link android.telecom.ConnectionService}.
* @hide
*/
- @SystemApi
public static final String EXTRA_SORT_ORDER =
"android.telecom.extra.SORT_ORDER";
@@ -89,7 +88,6 @@
* rather than cellular calls.
* @hide
*/
- @SystemApi
public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE =
"android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
@@ -114,7 +112,6 @@
*
* @hide
*/
- @SystemApi
public static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK =
"android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
@@ -163,7 +160,6 @@
* in progress.
* @hide
*/
- @SystemApi
public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
"android.telecom.extra.PLAY_CALL_RECORDING_TONE";
@@ -258,7 +254,6 @@
* See {@link #getCapabilities}
* @hide
*/
- @SystemApi
public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 0x80;
/**
@@ -282,7 +277,6 @@
* convert all outgoing video calls to emergency numbers to audio-only.
* @hide
*/
- @SystemApi
public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
/**
@@ -340,7 +334,6 @@
*
* @hide
*/
- @SystemApi
public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000;
/**
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 7f4fcc0..1792256 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -318,13 +318,13 @@
* the remote handle of the new call.
* @hide
*/
- @SystemApi
public static final String EXTRA_UNKNOWN_CALL_HANDLE =
"android.telecom.extra.UNKNOWN_CALL_HANDLE";
/**
* Optional extra for incoming and outgoing calls containing a long which specifies the time the
* call was created. This value is in milliseconds since boot.
+ * @hide
*/
public static final String EXTRA_CALL_CREATED_TIME_MILLIS =
"android.telecom.extra.CALL_CREATED_TIME_MILLIS";
@@ -388,7 +388,6 @@
* </ul>
* @hide
*/
- @SystemApi
public static final String EXTRA_CALL_TECHNOLOGY_TYPE =
"android.telecom.extra.CALL_TECHNOLOGY_TYPE";
@@ -731,7 +730,6 @@
* @see #EXTRA_CURRENT_TTY_MODE
* @hide
*/
- @SystemApi
public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
"android.telecom.action.CURRENT_TTY_MODE_CHANGED";
@@ -746,7 +744,6 @@
* </ul>
* @hide
*/
- @SystemApi
public static final String EXTRA_CURRENT_TTY_MODE =
"android.telecom.extra.CURRENT_TTY_MODE";
@@ -757,7 +754,6 @@
* @see #EXTRA_TTY_PREFERRED_MODE
* @hide
*/
- @SystemApi
public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
"android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
@@ -768,7 +764,6 @@
*
* @hide
*/
- @SystemApi
public static final String EXTRA_TTY_PREFERRED_MODE =
"android.telecom.extra.TTY_PREFERRED_MODE";
@@ -846,7 +841,6 @@
*
* @hide
*/
- @SystemApi
public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
/**
@@ -943,8 +937,8 @@
*/
public TelecomManager(Context context, ITelecomService telecomServiceImpl) {
Context appContext = context.getApplicationContext();
- if (appContext != null && Objects.equals(context.getFeatureId(),
- appContext.getFeatureId())) {
+ if (appContext != null && Objects.equals(context.getAttributionTag(),
+ appContext.getAttributionTag())) {
mContext = appContext;
} else {
mContext = context;
@@ -978,7 +972,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme,
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e);
@@ -1176,7 +1170,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e);
@@ -1202,7 +1196,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().getCallCapablePhoneAccounts(includeDisabledAccounts,
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" +
@@ -1506,7 +1500,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().isVoiceMailNumber(accountHandle, number,
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e);
@@ -1528,7 +1522,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().getVoiceMailNumber(accountHandle,
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e);
@@ -1549,7 +1543,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().getLine1Number(accountHandle,
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e);
@@ -1571,7 +1565,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().isInCall(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling isInCall().", e);
@@ -1597,7 +1591,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().isInManagedCall(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling isInManagedCall().", e);
@@ -1778,7 +1772,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().isTtySupported(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get TTY supported state.", e);
@@ -1803,7 +1797,7 @@
try {
if (isServiceConnected()) {
return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e);
@@ -2033,7 +2027,7 @@
if (service != null) {
try {
service.showInCallScreen(showDialpad, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
}
@@ -2096,7 +2090,7 @@
}
try {
service.placeCall(address, extras == null ? new Bundle() : extras,
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
new file mode 100644
index 0000000..a49d3d53
--- /dev/null
+++ b/telephony/api/system-current.txt
@@ -0,0 +1,2151 @@
+// Signature format: 2.0
+package android.telephony {
+
+ public final class AccessNetworkConstants {
+ field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff
+ }
+
+ public static final class AccessNetworkConstants.NgranBands {
+ method public static int getFrequencyRangeGroup(int);
+ field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1
+ field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2
+ field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
+ }
+
+ public final class BarringInfo implements android.os.Parcelable {
+ ctor public BarringInfo();
+ method @NonNull public android.telephony.BarringInfo createLocationInfoSanitizedCopy();
+ }
+
+ public final class CallAttributes implements android.os.Parcelable {
+ ctor public CallAttributes(@NonNull android.telephony.PreciseCallState, int, @NonNull android.telephony.CallQuality);
+ method public int describeContents();
+ method @NonNull public android.telephony.CallQuality getCallQuality();
+ method public int getNetworkType();
+ method @NonNull public android.telephony.PreciseCallState getPreciseCallState();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
+ }
+
+ public final class CallQuality implements android.os.Parcelable {
+ ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+ ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
+ method public int describeContents();
+ method public int getAverageRelativeJitter();
+ method public int getAverageRoundTripTime();
+ method public int getCallDuration();
+ method public int getCodecType();
+ method public int getDownlinkCallQualityLevel();
+ method public int getMaxRelativeJitter();
+ method public int getNumRtpPacketsNotReceived();
+ method public int getNumRtpPacketsReceived();
+ method public int getNumRtpPacketsTransmitted();
+ method public int getNumRtpPacketsTransmittedLost();
+ method public int getUplinkCallQualityLevel();
+ method public boolean isIncomingSilenceDetected();
+ method public boolean isOutgoingSilenceDetected();
+ method public boolean isRtpInactivityDetected();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CALL_QUALITY_BAD = 4; // 0x4
+ field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
+ field public static final int CALL_QUALITY_FAIR = 2; // 0x2
+ field public static final int CALL_QUALITY_GOOD = 1; // 0x1
+ field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5
+ field public static final int CALL_QUALITY_POOR = 3; // 0x3
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
+ }
+
+ public class CarrierConfigManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName();
+ method @NonNull public static android.os.PersistableBundle getDefaultConfig();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void updateConfigForPhoneId(int, String);
+ field public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
+ field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
+ }
+
+ public static final class CarrierConfigManager.Wifi {
+ field public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = "wifi.hotspot_maximum_client_count";
+ field public static final String KEY_PREFIX = "wifi.";
+ }
+
+ public final class CarrierRestrictionRules implements android.os.Parcelable {
+ method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers();
+ method public int getDefaultCarrierRestriction();
+ method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers();
+ method public int getMultiSimPolicy();
+ method public boolean isAllCarriersAllowed();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1
+ field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CarrierRestrictionRules> CREATOR;
+ field public static final int MULTISIM_POLICY_NONE = 0; // 0x0
+ field public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1; // 0x1
+ }
+
+ public static final class CarrierRestrictionRules.Builder {
+ ctor public CarrierRestrictionRules.Builder();
+ method @NonNull public android.telephony.CarrierRestrictionRules build();
+ method @NonNull public android.telephony.CarrierRestrictionRules.Builder setAllCarriersAllowed();
+ method @NonNull public android.telephony.CarrierRestrictionRules.Builder setAllowedCarriers(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
+ method @NonNull public android.telephony.CarrierRestrictionRules.Builder setDefaultCarrierRestriction(int);
+ method @NonNull public android.telephony.CarrierRestrictionRules.Builder setExcludedCarriers(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
+ method @NonNull public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int);
+ }
+
+ public class CbGeoUtils {
+ }
+
+ public static class CbGeoUtils.Circle implements android.telephony.CbGeoUtils.Geometry {
+ ctor public CbGeoUtils.Circle(@NonNull android.telephony.CbGeoUtils.LatLng, double);
+ method public boolean contains(@NonNull android.telephony.CbGeoUtils.LatLng);
+ method @NonNull public android.telephony.CbGeoUtils.LatLng getCenter();
+ method public double getRadius();
+ }
+
+ public static interface CbGeoUtils.Geometry {
+ method public boolean contains(@NonNull android.telephony.CbGeoUtils.LatLng);
+ }
+
+ public static class CbGeoUtils.LatLng {
+ ctor public CbGeoUtils.LatLng(double, double);
+ method public double distance(@NonNull android.telephony.CbGeoUtils.LatLng);
+ method @NonNull public android.telephony.CbGeoUtils.LatLng subtract(@NonNull android.telephony.CbGeoUtils.LatLng);
+ field public final double lat;
+ field public final double lng;
+ }
+
+ public static class CbGeoUtils.Polygon implements android.telephony.CbGeoUtils.Geometry {
+ ctor public CbGeoUtils.Polygon(@NonNull java.util.List<android.telephony.CbGeoUtils.LatLng>);
+ method public boolean contains(@NonNull android.telephony.CbGeoUtils.LatLng);
+ method @NonNull public java.util.List<android.telephony.CbGeoUtils.LatLng> getVertices();
+ }
+
+ public abstract class CellBroadcastService extends android.app.Service {
+ ctor public CellBroadcastService();
+ method @NonNull @WorkerThread public abstract CharSequence getCellBroadcastAreaInfo(int);
+ method public android.os.IBinder onBind(@Nullable android.content.Intent);
+ method public abstract void onCdmaCellBroadcastSms(int, @NonNull byte[], int);
+ method public abstract void onCdmaScpMessage(int, @NonNull java.util.List<android.telephony.cdma.CdmaSmsCbProgramData>, @NonNull String, @NonNull java.util.function.Consumer<android.os.Bundle>);
+ method public abstract void onGsmCellBroadcastSms(int, @NonNull byte[]);
+ field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
+ }
+
+ public abstract class CellIdentity implements android.os.Parcelable {
+ method @NonNull public abstract android.telephony.CellLocation asCellLocation();
+ method @NonNull public abstract android.telephony.CellIdentity sanitizeLocationInfo();
+ }
+
+ public final class CellIdentityCdma extends android.telephony.CellIdentity {
+ method @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo();
+ }
+
+ public final class CellIdentityGsm extends android.telephony.CellIdentity {
+ method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityGsm sanitizeLocationInfo();
+ }
+
+ public final class CellIdentityLte extends android.telephony.CellIdentity {
+ method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityLte sanitizeLocationInfo();
+ }
+
+ public final class CellIdentityNr extends android.telephony.CellIdentity {
+ method @NonNull public android.telephony.CellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityNr sanitizeLocationInfo();
+ }
+
+ public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
+ method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityTdscdma sanitizeLocationInfo();
+ }
+
+ public final class CellIdentityWcdma extends android.telephony.CellIdentity {
+ method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
+ method @NonNull public android.telephony.CellIdentityWcdma sanitizeLocationInfo();
+ }
+
+ public final class DataFailCause {
+ field @Deprecated public static final int VSNCP_APN_UNATHORIZED = 2238; // 0x8be
+ }
+
+ public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
+ }
+
+ public final class DisconnectCause {
+ field public static final int ALREADY_DIALING = 72; // 0x48
+ field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
+ field public static final int BUSY = 4; // 0x4
+ field public static final int CALLING_DISABLED = 74; // 0x4a
+ field public static final int CALL_BARRED = 20; // 0x14
+ field public static final int CALL_PULLED = 51; // 0x33
+ field public static final int CANT_CALL_WHILE_RINGING = 73; // 0x49
+ field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
+ field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
+ field public static final int CDMA_ALREADY_ACTIVATED = 49; // 0x31
+ field public static final int CDMA_DROP = 27; // 0x1b
+ field public static final int CDMA_INTERCEPT = 28; // 0x1c
+ field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
+ field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22
+ field public static final int CDMA_PREEMPTED = 33; // 0x21
+ field public static final int CDMA_REORDER = 29; // 0x1d
+ field public static final int CDMA_RETRY_ORDER = 31; // 0x1f
+ field public static final int CDMA_SO_REJECT = 30; // 0x1e
+ field public static final int CONGESTION = 5; // 0x5
+ field public static final int CS_RESTRICTED = 22; // 0x16
+ field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
+ field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
+ field public static final int DATA_DISABLED = 54; // 0x36
+ field public static final int DATA_LIMIT_REACHED = 55; // 0x37
+ field public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57; // 0x39
+ field public static final int DIALED_MMI = 39; // 0x27
+ field public static final int DIAL_LOW_BATTERY = 62; // 0x3e
+ field public static final int DIAL_MODIFIED_TO_DIAL = 48; // 0x30
+ field public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; // 0x42
+ field public static final int DIAL_MODIFIED_TO_SS = 47; // 0x2f
+ field public static final int DIAL_MODIFIED_TO_USSD = 46; // 0x2e
+ field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; // 0x45
+ field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; // 0x46
+ field public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; // 0x43
+ field public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; // 0x44
+ field public static final int EMERGENCY_PERM_FAILURE = 64; // 0x40
+ field public static final int EMERGENCY_TEMP_FAILURE = 63; // 0x3f
+ field public static final int ERROR_UNSPECIFIED = 36; // 0x24
+ field public static final int FDN_BLOCKED = 21; // 0x15
+ field public static final int ICC_ERROR = 19; // 0x13
+ field public static final int IMEI_NOT_ACCEPTED = 58; // 0x3a
+ field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
+ field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
+ field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
+ field public static final int INCOMING_AUTO_REJECTED = 81; // 0x51
+ field public static final int INCOMING_MISSED = 1; // 0x1
+ field public static final int INCOMING_REJECTED = 16; // 0x10
+ field public static final int INVALID_CREDENTIALS = 10; // 0xa
+ field public static final int INVALID_NUMBER = 7; // 0x7
+ field public static final int LIMIT_EXCEEDED = 15; // 0xf
+ field public static final int LOCAL = 3; // 0x3
+ field public static final int LOST_SIGNAL = 14; // 0xe
+ field public static final int LOW_BATTERY = 61; // 0x3d
+ field public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; // 0x35
+ field public static final int MMI = 6; // 0x6
+ field public static final int NORMAL = 2; // 0x2
+ field public static final int NORMAL_UNSPECIFIED = 65; // 0x41
+ field public static final int NOT_DISCONNECTED = 0; // 0x0
+ field public static final int NOT_VALID = -1; // 0xffffffff
+ field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
+ field public static final int NUMBER_UNREACHABLE = 8; // 0x8
+ field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c
+ field public static final int OUTGOING_CANCELED = 44; // 0x2c
+ field public static final int OUTGOING_EMERGENCY_CALL_PLACED = 80; // 0x50
+ field public static final int OUTGOING_FAILURE = 43; // 0x2b
+ field public static final int OUT_OF_NETWORK = 11; // 0xb
+ field public static final int OUT_OF_SERVICE = 18; // 0x12
+ field public static final int POWER_OFF = 17; // 0x11
+ field public static final int SERVER_ERROR = 12; // 0xc
+ field public static final int SERVER_UNREACHABLE = 9; // 0x9
+ field public static final int TIMED_OUT = 13; // 0xd
+ field public static final int TOO_MANY_ONGOING_CALLS = 75; // 0x4b
+ field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
+ field public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; // 0x32
+ field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
+ field public static final int WIFI_LOST = 59; // 0x3b
+ }
+
+ public final class ImsiEncryptionInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getKeyIdentifier();
+ method @Nullable public java.security.PublicKey getPublicKey();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ImsiEncryptionInfo> CREATOR;
+ }
+
+ public final class LteVopsSupportInfo implements android.os.Parcelable {
+ ctor public LteVopsSupportInfo(int, int);
+ method public int describeContents();
+ method public int getEmcBearerSupport();
+ method public int getVopsSupport();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LteVopsSupportInfo> CREATOR;
+ field public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1
+ field public static final int LTE_STATUS_NOT_SUPPORTED = 3; // 0x3
+ field public static final int LTE_STATUS_SUPPORTED = 2; // 0x2
+ }
+
+ public class MbmsDownloadSession implements java.lang.AutoCloseable {
+ field public static final String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
+ }
+
+ public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+ field public static final String MBMS_GROUP_CALL_SERVICE_ACTION = "android.telephony.action.EmbmsGroupCall";
+ }
+
+ public class MbmsStreamingSession implements java.lang.AutoCloseable {
+ field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
+ }
+
+ public final class ModemActivityInfo implements android.os.Parcelable {
+ ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
+ method public int describeContents();
+ method public int getIdleTimeMillis();
+ method public int getReceiveTimeMillis();
+ method public int getSleepTimeMillis();
+ method public long getTimestamp();
+ method @NonNull public java.util.List<android.telephony.ModemActivityInfo.TransmitPower> getTransmitPowerInfo();
+ method public boolean isValid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+ field public static final int TX_POWER_LEVELS = 5; // 0x5
+ field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+ field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+ field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+ field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+ field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+ }
+
+ public class ModemActivityInfo.TransmitPower {
+ method @NonNull public android.util.Range<java.lang.Integer> getPowerRangeInDbm();
+ method public int getTimeInMillis();
+ }
+
+ public final class NetworkRegistrationInfo implements android.os.Parcelable {
+ method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
+ method public int getRegistrationState();
+ method public int getRejectCause();
+ method public int getRoamingType();
+ method public boolean isEmergencyEnabled();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3
+ field public static final int REGISTRATION_STATE_HOME = 1; // 0x1
+ field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0
+ field public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; // 0x2
+ field public static final int REGISTRATION_STATE_ROAMING = 5; // 0x5
+ field public static final int REGISTRATION_STATE_UNKNOWN = 4; // 0x4
+ }
+
+ public static final class NetworkRegistrationInfo.Builder {
+ ctor public NetworkRegistrationInfo.Builder();
+ method @NonNull public android.telephony.NetworkRegistrationInfo build();
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAccessNetworkTechnology(int);
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAvailableServices(@NonNull java.util.List<java.lang.Integer>);
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setCellIdentity(@Nullable android.telephony.CellIdentity);
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setDomain(int);
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setEmergencyOnly(boolean);
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegisteredPlmn(@Nullable String);
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegistrationState(int);
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRejectCause(int);
+ method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int);
+ }
+
+ public abstract class NetworkService extends android.app.Service {
+ ctor public NetworkService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method @Nullable public abstract android.telephony.NetworkService.NetworkServiceProvider onCreateNetworkServiceProvider(int);
+ field public static final String SERVICE_INTERFACE = "android.telephony.NetworkService";
+ }
+
+ public abstract class NetworkService.NetworkServiceProvider implements java.lang.AutoCloseable {
+ ctor public NetworkService.NetworkServiceProvider(int);
+ method public abstract void close();
+ method public final int getSlotIndex();
+ method public final void notifyNetworkRegistrationInfoChanged();
+ method public void requestNetworkRegistrationInfo(int, @NonNull android.telephony.NetworkServiceCallback);
+ }
+
+ public class NetworkServiceCallback {
+ method public void onRequestNetworkRegistrationInfoComplete(int, @Nullable android.telephony.NetworkRegistrationInfo);
+ field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+ field public static final int RESULT_ERROR_FAILED = 5; // 0x5
+ field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+ field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+ field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ }
+
+ public interface NumberVerificationCallback {
+ method public default void onCallReceived(@NonNull String);
+ method public default void onVerificationFailed(int);
+ field public static final int REASON_CONCURRENT_REQUESTS = 4; // 0x4
+ field public static final int REASON_IN_ECBM = 5; // 0x5
+ field public static final int REASON_IN_EMERGENCY_CALL = 6; // 0x6
+ field public static final int REASON_NETWORK_NOT_AVAILABLE = 2; // 0x2
+ field public static final int REASON_TIMED_OUT = 1; // 0x1
+ field public static final int REASON_TOO_MANY_CALLS = 3; // 0x3
+ field public static final int REASON_UNSPECIFIED = 0; // 0x0
+ }
+
+ public final class PhoneNumberRange implements android.os.Parcelable {
+ ctor public PhoneNumberRange(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
+ method public int describeContents();
+ method public boolean matches(@NonNull String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneNumberRange> CREATOR;
+ }
+
+ public class PhoneNumberUtils {
+ method @NonNull public static String getUsernameFromUriNumber(@NonNull String);
+ method public static boolean isUriNumber(@Nullable String);
+ method public static boolean isVoiceMailNumber(@NonNull android.content.Context, int, @Nullable String);
+ }
+
+ public final class PreciseCallState implements android.os.Parcelable {
+ ctor public PreciseCallState(int, int, int, int, int);
+ method public int describeContents();
+ method public int getBackgroundCallState();
+ method public int getForegroundCallState();
+ method public int getRingingCallState();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseCallState> CREATOR;
+ field public static final int PRECISE_CALL_STATE_ACTIVE = 1; // 0x1
+ field public static final int PRECISE_CALL_STATE_ALERTING = 4; // 0x4
+ field public static final int PRECISE_CALL_STATE_DIALING = 3; // 0x3
+ field public static final int PRECISE_CALL_STATE_DISCONNECTED = 7; // 0x7
+ field public static final int PRECISE_CALL_STATE_DISCONNECTING = 8; // 0x8
+ field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2
+ field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0
+ field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5
+ field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff
+ field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
+ }
+
+ public final class PreciseDataConnectionState implements android.os.Parcelable {
+ method @Deprecated @NonNull public String getDataConnectionApn();
+ method @Deprecated public int getDataConnectionApnTypeBitMask();
+ method @Deprecated public int getDataConnectionFailCause();
+ method @Deprecated @Nullable public android.net.LinkProperties getDataConnectionLinkProperties();
+ method @Deprecated public int getDataConnectionNetworkType();
+ method @Deprecated public int getDataConnectionState();
+ }
+
+ public final class PreciseDisconnectCause {
+ field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
+ field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
+ field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44
+ field public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; // 0x39
+ field public static final int BEARER_NOT_AVAIL = 58; // 0x3a
+ field public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; // 0x41
+ field public static final int BUSY = 17; // 0x11
+ field public static final int CALL_BARRED = 240; // 0xf0
+ field public static final int CALL_REJECTED = 21; // 0x15
+ field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
+ field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
+ field public static final int CDMA_DROP = 1001; // 0x3e9
+ field public static final int CDMA_INTERCEPT = 1002; // 0x3ea
+ field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
+ field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
+ field public static final int CDMA_PREEMPTED = 1007; // 0x3ef
+ field public static final int CDMA_REORDER = 1003; // 0x3eb
+ field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
+ field public static final int CDMA_SO_REJECT = 1004; // 0x3ec
+ field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c
+ field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6
+ field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+ field public static final int DESTINATION_OUT_OF_ORDER = 27; // 0x1b
+ field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+ field public static final int FACILITY_REJECTED = 29; // 0x1d
+ field public static final int FDN_BLOCKED = 241; // 0xf1
+ field public static final int IMEI_NOT_ACCEPTED = 243; // 0xf3
+ field public static final int IMSI_UNKNOWN_IN_VLR = 242; // 0xf2
+ field public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55; // 0x37
+ field public static final int INCOMPATIBLE_DESTINATION = 88; // 0x58
+ field public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99; // 0x63
+ field public static final int INTERWORKING_UNSPECIFIED = 127; // 0x7f
+ field public static final int INVALID_MANDATORY_INFORMATION = 96; // 0x60
+ field public static final int INVALID_NUMBER_FORMAT = 28; // 0x1c
+ field public static final int INVALID_TRANSACTION_IDENTIFIER = 81; // 0x51
+ field public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101; // 0x65
+ field public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97; // 0x61
+ field public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98; // 0x62
+ field public static final int NETWORK_DETACH = 261; // 0x105
+ field public static final int NETWORK_OUT_OF_ORDER = 38; // 0x26
+ field public static final int NETWORK_REJECT = 252; // 0xfc
+ field public static final int NETWORK_RESP_TIMEOUT = 251; // 0xfb
+ field public static final int NORMAL = 16; // 0x10
+ field public static final int NORMAL_UNSPECIFIED = 31; // 0x1f
+ field public static final int NOT_VALID = -1; // 0xffffffff
+ field public static final int NO_ANSWER_FROM_USER = 19; // 0x13
+ field public static final int NO_CIRCUIT_AVAIL = 34; // 0x22
+ field public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0; // 0x0
+ field public static final int NO_ROUTE_TO_DESTINATION = 3; // 0x3
+ field public static final int NO_USER_RESPONDING = 18; // 0x12
+ field public static final int NO_VALID_SIM = 249; // 0xf9
+ field public static final int NUMBER_CHANGED = 22; // 0x16
+ field public static final int OEM_CAUSE_1 = 61441; // 0xf001
+ field public static final int OEM_CAUSE_10 = 61450; // 0xf00a
+ field public static final int OEM_CAUSE_11 = 61451; // 0xf00b
+ field public static final int OEM_CAUSE_12 = 61452; // 0xf00c
+ field public static final int OEM_CAUSE_13 = 61453; // 0xf00d
+ field public static final int OEM_CAUSE_14 = 61454; // 0xf00e
+ field public static final int OEM_CAUSE_15 = 61455; // 0xf00f
+ field public static final int OEM_CAUSE_2 = 61442; // 0xf002
+ field public static final int OEM_CAUSE_3 = 61443; // 0xf003
+ field public static final int OEM_CAUSE_4 = 61444; // 0xf004
+ field public static final int OEM_CAUSE_5 = 61445; // 0xf005
+ field public static final int OEM_CAUSE_6 = 61446; // 0xf006
+ field public static final int OEM_CAUSE_7 = 61447; // 0xf007
+ field public static final int OEM_CAUSE_8 = 61448; // 0xf008
+ field public static final int OEM_CAUSE_9 = 61449; // 0xf009
+ field public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70; // 0x46
+ field public static final int OPERATOR_DETERMINED_BARRING = 8; // 0x8
+ field public static final int OUT_OF_SRV = 248; // 0xf8
+ field public static final int PREEMPTION = 25; // 0x19
+ field public static final int PROTOCOL_ERROR_UNSPECIFIED = 111; // 0x6f
+ field public static final int QOS_NOT_AVAIL = 49; // 0x31
+ field public static final int RADIO_ACCESS_FAILURE = 253; // 0xfd
+ field public static final int RADIO_INTERNAL_ERROR = 250; // 0xfa
+ field public static final int RADIO_LINK_FAILURE = 254; // 0xfe
+ field public static final int RADIO_LINK_LOST = 255; // 0xff
+ field public static final int RADIO_OFF = 247; // 0xf7
+ field public static final int RADIO_RELEASE_ABNORMAL = 259; // 0x103
+ field public static final int RADIO_RELEASE_NORMAL = 258; // 0x102
+ field public static final int RADIO_SETUP_FAILURE = 257; // 0x101
+ field public static final int RADIO_UPLINK_FAILURE = 256; // 0x100
+ field public static final int RECOVERY_ON_TIMER_EXPIRED = 102; // 0x66
+ field public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69; // 0x45
+ field public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50; // 0x32
+ field public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47; // 0x2f
+ field public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95; // 0x5f
+ field public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; // 0x3f
+ field public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79; // 0x4f
+ field public static final int STATUS_ENQUIRY = 30; // 0x1e
+ field public static final int SWITCHING_CONGESTION = 42; // 0x2a
+ field public static final int TEMPORARY_FAILURE = 41; // 0x29
+ field public static final int UNOBTAINABLE_NUMBER = 1; // 0x1
+ field public static final int USER_NOT_MEMBER_OF_CUG = 87; // 0x57
+ }
+
+ public class ServiceState implements android.os.Parcelable {
+ method public void fillInNotifierBundle(@NonNull android.os.Bundle);
+ method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
+ method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
+ method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
+ method @NonNull public static android.telephony.ServiceState newFromBundle(@NonNull android.os.Bundle);
+ field public static final int ROAMING_TYPE_DOMESTIC = 2; // 0x2
+ field public static final int ROAMING_TYPE_INTERNATIONAL = 3; // 0x3
+ field public static final int ROAMING_TYPE_NOT_ROAMING = 0; // 0x0
+ field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
+ }
+
+ public class SignalStrength implements android.os.Parcelable {
+ ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
+ }
+
+ public final class SmsCbCmasInfo implements android.os.Parcelable {
+ ctor public SmsCbCmasInfo(int, int, int, int, int, int);
+ method public int describeContents();
+ method public int getCategory();
+ method public int getCertainty();
+ method public int getMessageClass();
+ method public int getResponseType();
+ method public int getSeverity();
+ method public int getUrgency();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CMAS_CATEGORY_CBRNE = 10; // 0xa
+ field public static final int CMAS_CATEGORY_ENV = 7; // 0x7
+ field public static final int CMAS_CATEGORY_FIRE = 5; // 0x5
+ field public static final int CMAS_CATEGORY_GEO = 0; // 0x0
+ field public static final int CMAS_CATEGORY_HEALTH = 6; // 0x6
+ field public static final int CMAS_CATEGORY_INFRA = 9; // 0x9
+ field public static final int CMAS_CATEGORY_MET = 1; // 0x1
+ field public static final int CMAS_CATEGORY_OTHER = 11; // 0xb
+ field public static final int CMAS_CATEGORY_RESCUE = 4; // 0x4
+ field public static final int CMAS_CATEGORY_SAFETY = 2; // 0x2
+ field public static final int CMAS_CATEGORY_SECURITY = 3; // 0x3
+ field public static final int CMAS_CATEGORY_TRANSPORT = 8; // 0x8
+ field public static final int CMAS_CATEGORY_UNKNOWN = -1; // 0xffffffff
+ field public static final int CMAS_CERTAINTY_LIKELY = 1; // 0x1
+ field public static final int CMAS_CERTAINTY_OBSERVED = 0; // 0x0
+ field public static final int CMAS_CERTAINTY_UNKNOWN = -1; // 0xffffffff
+ field public static final int CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY = 3; // 0x3
+ field public static final int CMAS_CLASS_CMAS_EXERCISE = 5; // 0x5
+ field public static final int CMAS_CLASS_EXTREME_THREAT = 1; // 0x1
+ field public static final int CMAS_CLASS_OPERATOR_DEFINED_USE = 6; // 0x6
+ field public static final int CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT = 0; // 0x0
+ field public static final int CMAS_CLASS_REQUIRED_MONTHLY_TEST = 4; // 0x4
+ field public static final int CMAS_CLASS_SEVERE_THREAT = 2; // 0x2
+ field public static final int CMAS_CLASS_UNKNOWN = -1; // 0xffffffff
+ field public static final int CMAS_RESPONSE_TYPE_ASSESS = 6; // 0x6
+ field public static final int CMAS_RESPONSE_TYPE_AVOID = 5; // 0x5
+ field public static final int CMAS_RESPONSE_TYPE_EVACUATE = 1; // 0x1
+ field public static final int CMAS_RESPONSE_TYPE_EXECUTE = 3; // 0x3
+ field public static final int CMAS_RESPONSE_TYPE_MONITOR = 4; // 0x4
+ field public static final int CMAS_RESPONSE_TYPE_NONE = 7; // 0x7
+ field public static final int CMAS_RESPONSE_TYPE_PREPARE = 2; // 0x2
+ field public static final int CMAS_RESPONSE_TYPE_SHELTER = 0; // 0x0
+ field public static final int CMAS_RESPONSE_TYPE_UNKNOWN = -1; // 0xffffffff
+ field public static final int CMAS_SEVERITY_EXTREME = 0; // 0x0
+ field public static final int CMAS_SEVERITY_SEVERE = 1; // 0x1
+ field public static final int CMAS_SEVERITY_UNKNOWN = -1; // 0xffffffff
+ field public static final int CMAS_URGENCY_EXPECTED = 1; // 0x1
+ field public static final int CMAS_URGENCY_IMMEDIATE = 0; // 0x0
+ field public static final int CMAS_URGENCY_UNKNOWN = -1; // 0xffffffff
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SmsCbCmasInfo> CREATOR;
+ }
+
+ public final class SmsCbEtwsInfo implements android.os.Parcelable {
+ ctor public SmsCbEtwsInfo(int, boolean, boolean, boolean, @Nullable byte[]);
+ method public int describeContents();
+ method @Nullable public byte[] getPrimaryNotificationSignature();
+ method public long getPrimaryNotificationTimestamp();
+ method public int getWarningType();
+ method public boolean isEmergencyUserAlert();
+ method public boolean isPopupAlert();
+ method public boolean isPrimary();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SmsCbEtwsInfo> CREATOR;
+ field public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0; // 0x0
+ field public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 2; // 0x2
+ field public static final int ETWS_WARNING_TYPE_OTHER_EMERGENCY = 4; // 0x4
+ field public static final int ETWS_WARNING_TYPE_TEST_MESSAGE = 3; // 0x3
+ field public static final int ETWS_WARNING_TYPE_TSUNAMI = 1; // 0x1
+ field public static final int ETWS_WARNING_TYPE_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public final class SmsCbLocation implements android.os.Parcelable {
+ ctor public SmsCbLocation(@NonNull String, int, int);
+ method public int describeContents();
+ method public int getCid();
+ method public int getLac();
+ method @NonNull public String getPlmn();
+ method public boolean isInLocationArea(@NonNull android.telephony.SmsCbLocation);
+ method public boolean isInLocationArea(@Nullable String, int, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SmsCbLocation> CREATOR;
+ }
+
+ public final class SmsCbMessage implements android.os.Parcelable {
+ ctor public SmsCbMessage(int, int, int, @NonNull android.telephony.SmsCbLocation, int, @Nullable String, int, @Nullable String, int, @Nullable android.telephony.SmsCbEtwsInfo, @Nullable android.telephony.SmsCbCmasInfo, int, @Nullable java.util.List<android.telephony.CbGeoUtils.Geometry>, long, int, int);
+ method @NonNull public static android.telephony.SmsCbMessage createFromCursor(@NonNull android.database.Cursor);
+ method public int describeContents();
+ method @Nullable public android.telephony.SmsCbCmasInfo getCmasWarningInfo();
+ method @NonNull public android.content.ContentValues getContentValues();
+ method public int getDataCodingScheme();
+ method @Nullable public android.telephony.SmsCbEtwsInfo getEtwsWarningInfo();
+ method public int getGeographicalScope();
+ method @NonNull public java.util.List<android.telephony.CbGeoUtils.Geometry> getGeometries();
+ method @Nullable public String getLanguageCode();
+ method @NonNull public android.telephony.SmsCbLocation getLocation();
+ method public int getMaximumWaitingDuration();
+ method @Nullable public String getMessageBody();
+ method public int getMessageFormat();
+ method public int getMessagePriority();
+ method public long getReceivedTime();
+ method public int getSerialNumber();
+ method public int getServiceCategory();
+ method public int getSlotIndex();
+ method public int getSubscriptionId();
+ method public boolean isCmasMessage();
+ method public boolean isEmergencyMessage();
+ method public boolean isEtwsMessage();
+ method public boolean needGeoFencingCheck();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SmsCbMessage> CREATOR;
+ field public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3; // 0x3
+ field public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0; // 0x0
+ field public static final int GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE = 2; // 0x2
+ field public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1; // 0x1
+ field public static final int MAXIMUM_WAIT_TIME_NOT_SET = 255; // 0xff
+ field public static final int MESSAGE_FORMAT_3GPP = 1; // 0x1
+ field public static final int MESSAGE_FORMAT_3GPP2 = 2; // 0x2
+ field public static final int MESSAGE_PRIORITY_EMERGENCY = 3; // 0x3
+ field public static final int MESSAGE_PRIORITY_INTERACTIVE = 1; // 0x1
+ field public static final int MESSAGE_PRIORITY_NORMAL = 0; // 0x0
+ field public static final int MESSAGE_PRIORITY_URGENT = 2; // 0x2
+ }
+
+ public final class SmsManager {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean copyMessageToIcc(@Nullable byte[], @NonNull byte[], int);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean deleteMessageFromIcc(int);
+ method public boolean disableCellBroadcastRange(int, int, int);
+ method public boolean enableCellBroadcastRange(int, int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public java.util.List<android.telephony.SmsMessage> getMessagesFromIcc();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
+ field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
+ field public static final int PREMIUM_SMS_CONSENT_ASK_USER = 1; // 0x1
+ field public static final int PREMIUM_SMS_CONSENT_NEVER_ALLOW = 2; // 0x2
+ field public static final int PREMIUM_SMS_CONSENT_UNKNOWN = 0; // 0x0
+ }
+
+ public class SmsMessage {
+ method @Nullable public static android.telephony.SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[], boolean);
+ method @Nullable public static android.telephony.SmsMessage.SubmitPdu getSmsPdu(int, int, @Nullable String, @NonNull String, @NonNull String, long);
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0, to=255) int, @IntRange(from=1, to=255) int, @IntRange(from=1, to=255) int);
+ }
+
+ public class SubscriptionInfo implements android.os.Parcelable {
+ method public boolean areUiccApplicationsEnabled();
+ method @Nullable public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
+ method public int getProfileClass();
+ method public boolean isGroupDisabled();
+ }
+
+ public class SubscriptionManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean canDisablePhysicalSubscription();
+ method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
+ method @NonNull public int[] getActiveAndHiddenSubscriptionIdList();
+ method @NonNull public int[] getActiveSubscriptionIdList();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
+ method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
+ method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
+ method public void requestEmbeddedSubscriptionInfoListRefresh();
+ method public void requestEmbeddedSubscriptionInfoListRefresh(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setDisplayName(@Nullable String, int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIconTint(int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
+ field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
+ field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
+ field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
+ field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
+ field public static final int PROFILE_CLASS_TESTING = 0; // 0x0
+ field public static final int PROFILE_CLASS_UNSET = -1; // 0xffffffff
+ field @NonNull public static final android.net.Uri VT_ENABLED_CONTENT_URI;
+ field @NonNull public static final android.net.Uri WFC_ENABLED_CONTENT_URI;
+ field @NonNull public static final android.net.Uri WFC_MODE_CONTENT_URI;
+ field @NonNull public static final android.net.Uri WFC_ROAMING_ENABLED_CONTENT_URI;
+ field @NonNull public static final android.net.Uri WFC_ROAMING_MODE_CONTENT_URI;
+ }
+
+ public class TelephonyFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ method public static void setTelephonyServiceManager(@NonNull android.os.TelephonyServiceManager);
+ }
+
+ public final class TelephonyHistogram implements android.os.Parcelable {
+ ctor public TelephonyHistogram(int, int, int);
+ ctor public TelephonyHistogram(android.telephony.TelephonyHistogram);
+ ctor public TelephonyHistogram(android.os.Parcel);
+ method public void addTimeTaken(int);
+ method public int describeContents();
+ method public int getAverageTime();
+ method public int getBucketCount();
+ method public int[] getBucketCounters();
+ method public int[] getBucketEndPoints();
+ method public int getCategory();
+ method public int getId();
+ method public int getMaxTime();
+ method public int getMinTime();
+ method public int getSampleCount();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.TelephonyHistogram> CREATOR;
+ field public static final int TELEPHONY_CATEGORY_RIL = 1; // 0x1
+ }
+
+ public class TelephonyManager {
+ method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
+ method public int checkCarrierPrivilegesForPackage(String);
+ method public int checkCarrierPrivilegesForPackageAnyPhone(String);
+ method public void dial(String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean disableDataConnectivity();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableModemForSlot(int, boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
+ method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
+ method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
+ method public String getCdmaPrlVersion();
+ method public int getCurrentPhoneType();
+ method public int getCurrentPhoneType(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
+ method @Deprecated public boolean getDataEnabled();
+ method @Deprecated public boolean getDataEnabled(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
+ method public int getMaxNumberOfSimultaneouslyActiveSims();
+ method public static long getMaxNumberVerificationTimeoutMillis();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
+ method public int getSimApplicationState();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimApplicationState(int);
+ method public int getSimCardState();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
+ method @Nullable public android.os.Bundle getVisualVoicemailSettings();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoiceActivationState();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int, String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
+ method public boolean isDataConnectivityPossible();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
+ method public boolean needsOtaServiceProvisioning();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyUserActivity();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method public void requestModemActivityInfo(@NonNull android.os.ResultReceiver);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int);
+ method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
+ method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
+ method @Deprecated public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoiceActivationState(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void shutdownAllRadios();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPin(String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
+ method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
+ method public void updateServiceLocation();
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
+ field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
+ field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
+ field public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED";
+ field public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
+ field public static final String ACTION_CARRIER_SIGNAL_RESET = "com.android.internal.telephony.CARRIER_SIGNAL_RESET";
+ field public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+ field public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
+ field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
+ field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
+ field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
+ field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
+ field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
+ field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
+ field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+ field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
+ field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
+ field public static final int CARD_POWER_DOWN = 0; // 0x0
+ field public static final int CARD_POWER_UP = 1; // 0x1
+ field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
+ field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
+ field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
+ field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
+ field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+ field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
+ field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
+ field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
+ field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
+ field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
+ field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
+ field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
+ field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
+ field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
+ field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4
+ field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA = 1; // 0x1
+ field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE = 0; // 0x0
+ field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS = 3; // 0x3
+ field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE = 2; // 0x2
+ field public static final String EXTRA_ERROR_CODE = "errorCode";
+ field public static final String EXTRA_PCO_ID = "pcoId";
+ field public static final String EXTRA_PCO_VALUE = "pcoValue";
+ field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
+ field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
+ field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
+ field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
+ field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
+ field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
+ field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES";
+ field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
+ field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1
+ field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0
+ field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
+ field public static final String EXTRA_SPN = "android.telephony.extra.SPN";
+ field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
+ field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+ field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
+ field public static final int KEY_TYPE_EPDG = 1; // 0x1
+ field public static final int KEY_TYPE_WLAN = 2; // 0x2
+ field public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
+ field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
+ field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
+ field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
+ field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L
+ field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L
+ field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L
+ field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L
+ field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L
+ field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L
+ field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L
+ field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
+ field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
+ field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
+ field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
+ field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
+ field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
+ field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
+ field public static final int RADIO_POWER_OFF = 0; // 0x0
+ field public static final int RADIO_POWER_ON = 1; // 0x1
+ field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
+ field public static final int SET_CARRIER_RESTRICTION_ERROR = 2; // 0x2
+ field public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1; // 0x1
+ field public static final int SET_CARRIER_RESTRICTION_SUCCESS = 0; // 0x0
+ field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2
+ field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1
+ field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3
+ field public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4; // 0x4
+ field public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0; // 0x0
+ field public static final int SIM_STATE_LOADED = 10; // 0xa
+ field public static final int SIM_STATE_PRESENT = 11; // 0xb
+ field public static final int SRVCC_STATE_HANDOVER_CANCELED = 3; // 0x3
+ field public static final int SRVCC_STATE_HANDOVER_COMPLETED = 1; // 0x1
+ field public static final int SRVCC_STATE_HANDOVER_FAILED = 2; // 0x2
+ field public static final int SRVCC_STATE_HANDOVER_NONE = -1; // 0xffffffff
+ field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
+ }
+
+ public final class UiccAccessRule implements android.os.Parcelable {
+ ctor public UiccAccessRule(byte[], @Nullable String, long);
+ method public int describeContents();
+ method public int getCarrierPrivilegeStatus(android.content.pm.PackageInfo);
+ method public int getCarrierPrivilegeStatus(android.content.pm.Signature, String);
+ method public String getCertificateHexString();
+ method @Nullable public String getPackageName();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccAccessRule> CREATOR;
+ }
+
+ public class UiccSlotInfo implements android.os.Parcelable {
+ ctor @Deprecated public UiccSlotInfo(boolean, boolean, String, int, int, boolean);
+ method public int describeContents();
+ method public String getCardId();
+ method public int getCardStateInfo();
+ method public boolean getIsActive();
+ method public boolean getIsEuicc();
+ method public boolean getIsExtendedApduSupported();
+ method public int getLogicalSlotIdx();
+ method public boolean isRemovable();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
+ field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
+ field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2
+ field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
+ }
+
+ public abstract class VisualVoicemailService extends android.app.Service {
+ method public static final void sendVisualVoicemailSms(android.content.Context, android.telecom.PhoneAccountHandle, String, short, String, android.app.PendingIntent);
+ method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
+ }
+
+}
+
+package android.telephony.cdma {
+
+ public final class CdmaSmsCbProgramData implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCategory();
+ method public int getOperation();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 4099; // 0x1003
+ field public static final int CATEGORY_CMAS_EXTREME_THREAT = 4097; // 0x1001
+ field public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 4351; // 0x10ff
+ field public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 4096; // 0x1000
+ field public static final int CATEGORY_CMAS_SEVERE_THREAT = 4098; // 0x1002
+ field public static final int CATEGORY_CMAS_TEST_MESSAGE = 4100; // 0x1004
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.cdma.CdmaSmsCbProgramData> CREATOR;
+ field public static final int OPERATION_ADD_CATEGORY = 1; // 0x1
+ field public static final int OPERATION_CLEAR_CATEGORIES = 2; // 0x2
+ field public static final int OPERATION_DELETE_CATEGORY = 0; // 0x0
+ }
+
+}
+
+package android.telephony.data {
+
+ public final class DataCallResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.LinkAddress> getAddresses();
+ method public int getCause();
+ method @NonNull public java.util.List<java.net.InetAddress> getDnsAddresses();
+ method @NonNull public java.util.List<java.net.InetAddress> getGatewayAddresses();
+ method public int getId();
+ method @NonNull public String getInterfaceName();
+ method public int getLinkStatus();
+ method @Deprecated public int getMtu();
+ method public int getMtuV4();
+ method public int getMtuV6();
+ method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
+ method public int getProtocolType();
+ method public int getSuggestedRetryTime();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
+ field public static final int LINK_STATUS_ACTIVE = 2; // 0x2
+ field public static final int LINK_STATUS_DORMANT = 1; // 0x1
+ field public static final int LINK_STATUS_INACTIVE = 0; // 0x0
+ field public static final int LINK_STATUS_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public static final class DataCallResponse.Builder {
+ ctor public DataCallResponse.Builder();
+ method @NonNull public android.telephony.data.DataCallResponse build();
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setAddresses(@NonNull java.util.List<android.net.LinkAddress>);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setCause(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setDnsAddresses(@NonNull java.util.List<java.net.InetAddress>);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setGatewayAddresses(@NonNull java.util.List<java.net.InetAddress>);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setId(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@NonNull String);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setLinkStatus(int);
+ method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
+ }
+
+ public final class DataProfile implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getApn();
+ method public int getAuthType();
+ method public int getBearerBitmask();
+ method @Deprecated public int getMtu();
+ method public int getMtuV4();
+ method public int getMtuV6();
+ method @Nullable public String getPassword();
+ method public int getProfileId();
+ method public int getProtocolType();
+ method public int getRoamingProtocolType();
+ method public int getSupportedApnTypesBitmask();
+ method public int getType();
+ method @Nullable public String getUserName();
+ method public boolean isEnabled();
+ method public boolean isPersistent();
+ method public boolean isPreferred();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR;
+ field public static final int TYPE_3GPP = 1; // 0x1
+ field public static final int TYPE_3GPP2 = 2; // 0x2
+ field public static final int TYPE_COMMON = 0; // 0x0
+ }
+
+ public static final class DataProfile.Builder {
+ ctor public DataProfile.Builder();
+ method @NonNull public android.telephony.data.DataProfile build();
+ method @NonNull public android.telephony.data.DataProfile.Builder enable(boolean);
+ method @NonNull public android.telephony.data.DataProfile.Builder setApn(@NonNull String);
+ method @NonNull public android.telephony.data.DataProfile.Builder setAuthType(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setBearerBitmask(int);
+ method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setMtu(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setMtuV4(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setMtuV6(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setPassword(@NonNull String);
+ method @NonNull public android.telephony.data.DataProfile.Builder setPersistent(boolean);
+ method @NonNull public android.telephony.data.DataProfile.Builder setPreferred(boolean);
+ method @NonNull public android.telephony.data.DataProfile.Builder setProfileId(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setProtocolType(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setRoamingProtocolType(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setSupportedApnTypesBitmask(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setType(int);
+ method @NonNull public android.telephony.data.DataProfile.Builder setUserName(@NonNull String);
+ }
+
+ public abstract class DataService extends android.app.Service {
+ ctor public DataService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method @Nullable public abstract android.telephony.data.DataService.DataServiceProvider onCreateDataServiceProvider(int);
+ field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
+ field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
+ field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
+ field public static final int REQUEST_REASON_UNKNOWN = 0; // 0x0
+ field public static final String SERVICE_INTERFACE = "android.telephony.data.DataService";
+ }
+
+ public abstract class DataService.DataServiceProvider implements java.lang.AutoCloseable {
+ ctor public DataService.DataServiceProvider(int);
+ method public abstract void close();
+ method public void deactivateDataCall(int, int, @Nullable android.telephony.data.DataServiceCallback);
+ method public final int getSlotIndex();
+ method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
+ method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
+ method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
+ method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
+ }
+
+ public class DataServiceCallback {
+ method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onDeactivateDataCallComplete(int);
+ method public void onRequestDataCallListComplete(int, @NonNull java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onSetDataProfileComplete(int);
+ method public void onSetInitialAttachApnComplete(int);
+ method public void onSetupDataCallComplete(int, @Nullable android.telephony.data.DataCallResponse);
+ field public static final int RESULT_ERROR_BUSY = 3; // 0x3
+ field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
+ field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
+ field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ }
+
+ public abstract class QualifiedNetworksService extends android.app.Service {
+ ctor public QualifiedNetworksService();
+ method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int);
+ field public static final String QUALIFIED_NETWORKS_SERVICE_INTERFACE = "android.telephony.data.QualifiedNetworksService";
+ }
+
+ public abstract class QualifiedNetworksService.NetworkAvailabilityProvider implements java.lang.AutoCloseable {
+ ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int);
+ method public abstract void close();
+ method public final int getSlotIndex();
+ method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
+ }
+
+}
+
+package android.telephony.euicc {
+
+ public final class DownloadableSubscription implements android.os.Parcelable {
+ method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
+ method @Nullable public String getCarrierName();
+ }
+
+ public static final class DownloadableSubscription.Builder {
+ ctor public DownloadableSubscription.Builder();
+ ctor public DownloadableSubscription.Builder(android.telephony.euicc.DownloadableSubscription);
+ method public android.telephony.euicc.DownloadableSubscription build();
+ method public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(java.util.List<android.telephony.UiccAccessRule>);
+ method public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(String);
+ method public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(String);
+ method public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(String);
+ }
+
+ public class EuiccCardManager {
+ method public void authenticateServer(String, String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void cancelSession(String, byte[], @android.telephony.euicc.EuiccCardManager.CancelReason int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void deleteProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void listNotifications(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
+ method public void loadBoundProfilePackage(String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void prepareDownload(String, @Nullable byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void removeNotificationFromList(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void requestAllProfiles(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo[]>);
+ method public void requestDefaultSmdpAddress(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
+ method public void requestEuiccChallenge(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void requestEuiccInfo1(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void requestEuiccInfo2(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
+ method public void requestProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
+ method public void requestRulesAuthTable(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccRulesAuthTable>);
+ method public void requestSmdsAddress(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
+ method public void resetMemory(String, @android.telephony.euicc.EuiccCardManager.ResetOption int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void retrieveNotification(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification>);
+ method public void retrieveNotificationList(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
+ method public void setDefaultSmdpAddress(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void setNickname(String, String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+ method public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
+ field public static final int CANCEL_REASON_END_USER_REJECTED = 0; // 0x0
+ field public static final int CANCEL_REASON_POSTPONED = 1; // 0x1
+ field public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; // 0x3
+ field public static final int CANCEL_REASON_TIMEOUT = 2; // 0x2
+ field public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 2; // 0x2
+ field public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1; // 0x1
+ field public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 4; // 0x4
+ field public static final int RESULT_CALLER_NOT_ALLOWED = -3; // 0xfffffffd
+ field public static final int RESULT_EUICC_NOT_FOUND = -2; // 0xfffffffe
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
+ }
+
+ @IntDef(prefix={"CANCEL_REASON_"}, value={android.telephony.euicc.EuiccCardManager.CANCEL_REASON_END_USER_REJECTED, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_POSTPONED, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_TIMEOUT, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_PPR_NOT_ALLOWED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccCardManager.CancelReason {
+ }
+
+ @IntDef(flag=true, prefix={"RESET_OPTION_"}, value={android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES, android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES, android.telephony.euicc.EuiccCardManager.RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccCardManager.ResetOption {
+ }
+
+ public static interface EuiccCardManager.ResultCallback<T> {
+ method public void onComplete(int, T);
+ }
+
+ public class EuiccManager {
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(android.content.Intent, android.os.Bundle);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@NonNull android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public java.util.List<java.lang.String> getSupportedCountries();
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public java.util.List<java.lang.String> getUnsupportedCountries();
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public boolean isSupportedCountry(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void setSupportedCountries(@NonNull java.util.List<java.lang.String>);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void setUnsupportedCountries(@NonNull java.util.List<java.lang.String>);
+ field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
+ field @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+ field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+ field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+ field public static final int EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED = 4; // 0x4
+ field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2
+ field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1
+ field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3
+ field public static final int EUICC_OTA_FAILED = 2; // 0x2
+ field public static final int EUICC_OTA_IN_PROGRESS = 1; // 0x1
+ field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4
+ field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5
+ field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
+ field public static final String EXTRA_ACTIVATION_TYPE = "android.telephony.euicc.extra.ACTIVATION_TYPE";
+ field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
+ field public static final String EXTRA_ENABLE_SUBSCRIPTION = "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
+ field public static final String EXTRA_FORCE_PROVISION = "android.telephony.euicc.extra.FORCE_PROVISION";
+ field public static final String EXTRA_FROM_SUBSCRIPTION_ID = "android.telephony.euicc.extra.FROM_SUBSCRIPTION_ID";
+ field public static final String EXTRA_PHYSICAL_SLOT_ID = "android.telephony.euicc.extra.PHYSICAL_SLOT_ID";
+ field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.euicc.extra.SUBSCRIPTION_ID";
+ field public static final String EXTRA_SUBSCRIPTION_NICKNAME = "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
+ }
+
+ @IntDef(prefix={"EUICC_OTA_"}, value={android.telephony.euicc.EuiccManager.EUICC_OTA_IN_PROGRESS, android.telephony.euicc.EuiccManager.EUICC_OTA_FAILED, android.telephony.euicc.EuiccManager.EUICC_OTA_SUCCEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_NOT_NEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccManager.OtaStatus {
+ }
+
+ public final class EuiccNotification implements android.os.Parcelable {
+ ctor public EuiccNotification(int, String, @android.telephony.euicc.EuiccNotification.Event int, @Nullable byte[]);
+ method public int describeContents();
+ method @Nullable public byte[] getData();
+ method @android.telephony.euicc.EuiccNotification.Event public int getEvent();
+ method public int getSeq();
+ method public String getTargetAddr();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @android.telephony.euicc.EuiccNotification.Event public static final int ALL_EVENTS = 15; // 0xf
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccNotification> CREATOR;
+ field public static final int EVENT_DELETE = 8; // 0x8
+ field public static final int EVENT_DISABLE = 4; // 0x4
+ field public static final int EVENT_ENABLE = 2; // 0x2
+ field public static final int EVENT_INSTALL = 1; // 0x1
+ }
+
+ @IntDef(flag=true, prefix={"EVENT_"}, value={android.telephony.euicc.EuiccNotification.EVENT_INSTALL, android.telephony.euicc.EuiccNotification.EVENT_ENABLE, android.telephony.euicc.EuiccNotification.EVENT_DISABLE, android.telephony.euicc.EuiccNotification.EVENT_DELETE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccNotification.Event {
+ }
+
+ public final class EuiccRulesAuthTable implements android.os.Parcelable {
+ method public int describeContents();
+ method public int findIndex(@android.service.euicc.EuiccProfileInfo.PolicyRule int, android.service.carrier.CarrierIdentifier);
+ method public boolean hasPolicyRuleFlag(int, @android.telephony.euicc.EuiccRulesAuthTable.PolicyRuleFlag int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccRulesAuthTable> CREATOR;
+ field public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1; // 0x1
+ }
+
+ public static final class EuiccRulesAuthTable.Builder {
+ ctor public EuiccRulesAuthTable.Builder(int);
+ method public android.telephony.euicc.EuiccRulesAuthTable.Builder add(int, java.util.List<android.service.carrier.CarrierIdentifier>, int);
+ method public android.telephony.euicc.EuiccRulesAuthTable build();
+ }
+
+ @IntDef(flag=true, prefix={"POLICY_RULE_FLAG_"}, value={android.telephony.euicc.EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccRulesAuthTable.PolicyRuleFlag {
+ }
+
+}
+
+package android.telephony.ims {
+
+ public final class ImsCallForwardInfo implements android.os.Parcelable {
+ ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int);
+ method public int describeContents();
+ method public int getCondition();
+ method public String getNumber();
+ method public int getServiceClass();
+ method public int getStatus();
+ method public int getTimeSeconds();
+ method public int getToA();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CDIV_CF_REASON_ALL = 4; // 0x4
+ field public static final int CDIV_CF_REASON_ALL_CONDITIONAL = 5; // 0x5
+ field public static final int CDIV_CF_REASON_BUSY = 1; // 0x1
+ field public static final int CDIV_CF_REASON_NOT_LOGGED_IN = 6; // 0x6
+ field public static final int CDIV_CF_REASON_NOT_REACHABLE = 3; // 0x3
+ field public static final int CDIV_CF_REASON_NO_REPLY = 2; // 0x2
+ field public static final int CDIV_CF_REASON_UNCONDITIONAL = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallForwardInfo> CREATOR;
+ field public static final int STATUS_ACTIVE = 1; // 0x1
+ field public static final int STATUS_NOT_ACTIVE = 0; // 0x0
+ field public static final int TYPE_OF_ADDRESS_INTERNATIONAL = 145; // 0x91
+ field public static final int TYPE_OF_ADDRESS_UNKNOWN = 129; // 0x81
+ }
+
+ public final class ImsCallProfile implements android.os.Parcelable {
+ ctor public ImsCallProfile();
+ ctor public ImsCallProfile(int, int);
+ ctor public ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile);
+ method public int describeContents();
+ method public String getCallExtra(String);
+ method public String getCallExtra(String, String);
+ method public boolean getCallExtraBoolean(String);
+ method public boolean getCallExtraBoolean(String, boolean);
+ method public int getCallExtraInt(String);
+ method public int getCallExtraInt(String, int);
+ method public android.os.Bundle getCallExtras();
+ method public int getCallType();
+ method public static int getCallTypeFromVideoState(int);
+ method public int getCallerNumberVerificationStatus();
+ method public int getEmergencyCallRouting();
+ method public int getEmergencyServiceCategories();
+ method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
+ method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
+ method @NonNull public android.os.Bundle getProprietaryCallExtras();
+ method public int getRestrictCause();
+ method public int getServiceType();
+ method public static int getVideoStateFromCallType(int);
+ method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
+ method public boolean hasKnownUserIntentEmergency();
+ method public boolean isEmergencyCallTesting();
+ method public boolean isVideoCall();
+ method public boolean isVideoPaused();
+ method public static int presentationToOir(int);
+ method public void setCallExtra(String, String);
+ method public void setCallExtraBoolean(String, boolean);
+ method public void setCallExtraInt(String, int);
+ method public void setCallRestrictCause(int);
+ method public void setCallerNumberVerificationStatus(int);
+ method public void setEmergencyCallRouting(int);
+ method public void setEmergencyCallTesting(boolean);
+ method public void setEmergencyServiceCategories(int);
+ method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>);
+ method public void setHasKnownUserIntentEmergency(boolean);
+ method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
+ method public void updateCallType(android.telephony.ims.ImsCallProfile);
+ method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CALL_RESTRICT_CAUSE_DISABLED = 2; // 0x2
+ field public static final int CALL_RESTRICT_CAUSE_HD = 3; // 0x3
+ field public static final int CALL_RESTRICT_CAUSE_NONE = 0; // 0x0
+ field public static final int CALL_RESTRICT_CAUSE_RAT = 1; // 0x1
+ field public static final int CALL_TYPE_VIDEO_N_VOICE = 3; // 0x3
+ field public static final int CALL_TYPE_VOICE = 2; // 0x2
+ field public static final int CALL_TYPE_VOICE_N_VIDEO = 1; // 0x1
+ field public static final int CALL_TYPE_VS = 8; // 0x8
+ field public static final int CALL_TYPE_VS_RX = 10; // 0xa
+ field public static final int CALL_TYPE_VS_TX = 9; // 0x9
+ field public static final int CALL_TYPE_VT = 4; // 0x4
+ field public static final int CALL_TYPE_VT_NODIR = 7; // 0x7
+ field public static final int CALL_TYPE_VT_RX = 6; // 0x6
+ field public static final int CALL_TYPE_VT_TX = 5; // 0x5
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallProfile> CREATOR;
+ field public static final int DIALSTRING_NORMAL = 0; // 0x0
+ field public static final int DIALSTRING_SS_CONF = 1; // 0x1
+ field public static final int DIALSTRING_USSD = 2; // 0x2
+ field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+ field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
+ field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
+ field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+ field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+ field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
+ field public static final String EXTRA_CNA = "cna";
+ field public static final String EXTRA_CNAP = "cnap";
+ field public static final String EXTRA_CODEC = "Codec";
+ field public static final String EXTRA_DIALSTRING = "dialstring";
+ field public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
+ field public static final String EXTRA_EMERGENCY_CALL = "e_call";
+ field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER";
+ field public static final String EXTRA_IS_CALL_PULL = "CallPull";
+ field public static final String EXTRA_OI = "oi";
+ field public static final String EXTRA_OIR = "oir";
+ field public static final String EXTRA_REMOTE_URI = "remote_uri";
+ field public static final String EXTRA_USSD = "ussd";
+ field public static final int OIR_DEFAULT = 0; // 0x0
+ field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2
+ field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
+ field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
+ field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
+ field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2
+ field public static final int SERVICE_TYPE_NONE = 0; // 0x0
+ field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1
+ field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2
+ field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0
+ field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1
+ }
+
+ public class ImsCallSessionListener {
+ method public void callQualityChanged(@NonNull android.telephony.CallQuality);
+ method public void callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+ method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+ method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
+ method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
+ method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
+ method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
+ method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
+ method public void callSessionInitiated(android.telephony.ims.ImsCallProfile);
+ method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionInviteParticipantsRequestDelivered();
+ method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
+ method @Deprecated public void callSessionMayHandover(int, int);
+ method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
+ method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
+ method public void callSessionMultipartyStateChanged(boolean);
+ method public void callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile);
+ method public void callSessionRemoveParticipantsRequestDelivered();
+ method public void callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
+ method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
+ method public void callSessionRttAudioIndicatorChanged(@NonNull android.telephony.ims.ImsStreamMediaProfile);
+ method public void callSessionRttMessageReceived(String);
+ method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
+ method public void callSessionRttModifyResponseReceived(int);
+ method public void callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification);
+ method public void callSessionTerminated(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionTtyModeReceived(int);
+ method public void callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo);
+ method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
+ method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
+ method public void callSessionUssdMessageReceived(int, String);
+ method public void onHandover(int, int, @Nullable android.telephony.ims.ImsReasonInfo);
+ method public void onHandoverFailed(int, int, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void onMayHandover(int, int);
+ }
+
+ public final class ImsConferenceState implements android.os.Parcelable {
+ method public int describeContents();
+ method public static int getConnectionStateForStatus(String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsConferenceState> CREATOR;
+ field public static final String DISPLAY_TEXT = "display-text";
+ field public static final String ENDPOINT = "endpoint";
+ field public static final String SIP_STATUS_CODE = "sipstatuscode";
+ field public static final String STATUS = "status";
+ field public static final String STATUS_ALERTING = "alerting";
+ field public static final String STATUS_CONNECTED = "connected";
+ field public static final String STATUS_CONNECT_FAIL = "connect-fail";
+ field public static final String STATUS_DIALING_IN = "dialing-in";
+ field public static final String STATUS_DIALING_OUT = "dialing-out";
+ field public static final String STATUS_DISCONNECTED = "disconnected";
+ field public static final String STATUS_DISCONNECTING = "disconnecting";
+ field public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
+ field public static final String STATUS_ON_HOLD = "on-hold";
+ field public static final String STATUS_PENDING = "pending";
+ field public static final String STATUS_SEND_ONLY = "sendonly";
+ field public static final String STATUS_SEND_RECV = "sendrecv";
+ field public static final String USER = "user";
+ field public final java.util.HashMap<java.lang.String,android.os.Bundle> mParticipants;
+ }
+
+ public final class ImsException extends java.lang.Exception {
+ ctor public ImsException(@Nullable String);
+ ctor public ImsException(@Nullable String, int);
+ ctor public ImsException(@Nullable String, int, @Nullable Throwable);
+ }
+
+ public final class ImsExternalCallState implements android.os.Parcelable {
+ ctor public ImsExternalCallState(@NonNull String, @NonNull android.net.Uri, @Nullable android.net.Uri, boolean, int, int, boolean);
+ method public int describeContents();
+ method @NonNull public android.net.Uri getAddress();
+ method public int getCallId();
+ method public int getCallState();
+ method public int getCallType();
+ method @Nullable public android.net.Uri getLocalAddress();
+ method public boolean isCallHeld();
+ method public boolean isCallPullable();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CALL_STATE_CONFIRMED = 1; // 0x1
+ field public static final int CALL_STATE_TERMINATED = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
+ }
+
+ public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
+ method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ }
+
+ @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback {
+ ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
+ }
+
+ public final class ImsReasonInfo implements android.os.Parcelable {
+ field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
+ }
+
+ public class ImsService extends android.app.Service {
+ ctor public ImsService();
+ method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
+ method public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
+ method public void disableIms(int);
+ method public void enableIms(int);
+ method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+ method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
+ method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
+ method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
+ method public void readyForFeatureCreation();
+ }
+
+ public final class ImsSsData implements android.os.Parcelable {
+ ctor public ImsSsData(int, int, int, int, int);
+ method public int describeContents();
+ method @Nullable public java.util.List<android.telephony.ims.ImsCallForwardInfo> getCallForwardInfo();
+ method public int getRequestType();
+ method public int getResult();
+ method public int getServiceClass();
+ method public int getServiceType();
+ method @NonNull public java.util.List<android.telephony.ims.ImsSsInfo> getSuppServiceInfo();
+ method public int getTeleserviceType();
+ method public boolean isTypeBarring();
+ method public boolean isTypeCf();
+ method public boolean isTypeClip();
+ method public boolean isTypeClir();
+ method public boolean isTypeColp();
+ method public boolean isTypeColr();
+ method public boolean isTypeCw();
+ method public boolean isTypeIcb();
+ method public boolean isTypeInterrogation();
+ method public boolean isTypeUnConditional();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsData> CREATOR;
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ field public static final int SERVICE_CLASS_DATA = 2; // 0x2
+ field public static final int SERVICE_CLASS_DATA_CIRCUIT_ASYNC = 32; // 0x20
+ field public static final int SERVICE_CLASS_DATA_CIRCUIT_SYNC = 16; // 0x10
+ field public static final int SERVICE_CLASS_DATA_PACKET_ACCESS = 64; // 0x40
+ field public static final int SERVICE_CLASS_DATA_PAD = 128; // 0x80
+ field public static final int SERVICE_CLASS_FAX = 4; // 0x4
+ field public static final int SERVICE_CLASS_NONE = 0; // 0x0
+ field public static final int SERVICE_CLASS_SMS = 8; // 0x8
+ field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+ field public static final int SS_ACTIVATION = 0; // 0x0
+ field public static final int SS_ALL_BARRING = 18; // 0x12
+ field public static final int SS_ALL_DATA_TELESERVICES = 3; // 0x3
+ field public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5; // 0x5
+ field public static final int SS_ALL_TELESEVICES = 1; // 0x1
+ field public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0; // 0x0
+ field public static final int SS_BAIC = 16; // 0x10
+ field public static final int SS_BAIC_ROAMING = 17; // 0x11
+ field public static final int SS_BAOC = 13; // 0xd
+ field public static final int SS_BAOIC = 14; // 0xe
+ field public static final int SS_BAOIC_EXC_HOME = 15; // 0xf
+ field public static final int SS_CFU = 0; // 0x0
+ field public static final int SS_CFUT = 6; // 0x6
+ field public static final int SS_CF_ALL = 4; // 0x4
+ field public static final int SS_CF_ALL_CONDITIONAL = 5; // 0x5
+ field public static final int SS_CF_BUSY = 1; // 0x1
+ field public static final int SS_CF_NOT_REACHABLE = 3; // 0x3
+ field public static final int SS_CF_NO_REPLY = 2; // 0x2
+ field public static final int SS_CLIP = 7; // 0x7
+ field public static final int SS_CLIR = 8; // 0x8
+ field public static final int SS_CNAP = 11; // 0xb
+ field public static final int SS_COLP = 9; // 0x9
+ field public static final int SS_COLR = 10; // 0xa
+ field public static final int SS_DEACTIVATION = 1; // 0x1
+ field public static final int SS_ERASURE = 4; // 0x4
+ field public static final int SS_INCOMING_BARRING = 20; // 0x14
+ field public static final int SS_INCOMING_BARRING_ANONYMOUS = 22; // 0x16
+ field public static final int SS_INCOMING_BARRING_DN = 21; // 0x15
+ field public static final int SS_INTERROGATION = 2; // 0x2
+ field public static final int SS_OUTGOING_BARRING = 19; // 0x13
+ field public static final int SS_REGISTRATION = 3; // 0x3
+ field public static final int SS_SMS_SERVICES = 4; // 0x4
+ field public static final int SS_TELEPHONY = 2; // 0x2
+ field public static final int SS_WAIT = 12; // 0xc
+ }
+
+ public static final class ImsSsData.Builder {
+ ctor public ImsSsData.Builder(int, int, int, int, int);
+ method @NonNull public android.telephony.ims.ImsSsData build();
+ method @NonNull public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(@NonNull java.util.List<android.telephony.ims.ImsCallForwardInfo>);
+ method @NonNull public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(@NonNull java.util.List<android.telephony.ims.ImsSsInfo>);
+ }
+
+ public final class ImsSsInfo implements android.os.Parcelable {
+ ctor @Deprecated public ImsSsInfo(int, @Nullable String);
+ method public int describeContents();
+ method public int getClirInterrogationStatus();
+ method public int getClirOutgoingState();
+ method @Deprecated public String getIcbNum();
+ method @Nullable public String getIncomingCommunicationBarringNumber();
+ method public int getProvisionStatus();
+ method public int getStatus();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CLIR_OUTGOING_DEFAULT = 0; // 0x0
+ field public static final int CLIR_OUTGOING_INVOCATION = 1; // 0x1
+ field public static final int CLIR_OUTGOING_SUPPRESSION = 2; // 0x2
+ field public static final int CLIR_STATUS_NOT_PROVISIONED = 0; // 0x0
+ field public static final int CLIR_STATUS_PROVISIONED_PERMANENT = 1; // 0x1
+ field public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4; // 0x4
+ field public static final int CLIR_STATUS_TEMPORARILY_RESTRICTED = 3; // 0x3
+ field public static final int CLIR_STATUS_UNKNOWN = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsInfo> CREATOR;
+ field public static final int DISABLED = 0; // 0x0
+ field public static final int ENABLED = 1; // 0x1
+ field public static final int NOT_REGISTERED = -1; // 0xffffffff
+ field public static final int SERVICE_NOT_PROVISIONED = 0; // 0x0
+ field public static final int SERVICE_PROVISIONED = 1; // 0x1
+ field public static final int SERVICE_PROVISIONING_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public static final class ImsSsInfo.Builder {
+ ctor public ImsSsInfo.Builder(int);
+ method @NonNull public android.telephony.ims.ImsSsInfo build();
+ method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirInterrogationStatus(int);
+ method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirOutgoingState(int);
+ method @NonNull public android.telephony.ims.ImsSsInfo.Builder setIncomingCommunicationBarringNumber(@NonNull String);
+ method @NonNull public android.telephony.ims.ImsSsInfo.Builder setProvisionStatus(int);
+ }
+
+ public final class ImsStreamMediaProfile implements android.os.Parcelable {
+ ctor public ImsStreamMediaProfile(int, int, int, int, int);
+ method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
+ method public int describeContents();
+ method public int getAudioDirection();
+ method public int getAudioQuality();
+ method public int getRttMode();
+ method public int getVideoDirection();
+ method public int getVideoQuality();
+ method public boolean isReceivingRttAudio();
+ method public boolean isRttCall();
+ method public void setReceivingRttAudio(boolean);
+ method public void setRttMode(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
+ field public static final int AUDIO_QUALITY_AMR_WB = 2; // 0x2
+ field public static final int AUDIO_QUALITY_EVRC = 4; // 0x4
+ field public static final int AUDIO_QUALITY_EVRC_B = 5; // 0x5
+ field public static final int AUDIO_QUALITY_EVRC_NW = 7; // 0x7
+ field public static final int AUDIO_QUALITY_EVRC_WB = 6; // 0x6
+ field public static final int AUDIO_QUALITY_EVS_FB = 20; // 0x14
+ field public static final int AUDIO_QUALITY_EVS_NB = 17; // 0x11
+ field public static final int AUDIO_QUALITY_EVS_SWB = 19; // 0x13
+ field public static final int AUDIO_QUALITY_EVS_WB = 18; // 0x12
+ field public static final int AUDIO_QUALITY_G711A = 13; // 0xd
+ field public static final int AUDIO_QUALITY_G711AB = 15; // 0xf
+ field public static final int AUDIO_QUALITY_G711U = 11; // 0xb
+ field public static final int AUDIO_QUALITY_G722 = 14; // 0xe
+ field public static final int AUDIO_QUALITY_G723 = 12; // 0xc
+ field public static final int AUDIO_QUALITY_G729 = 16; // 0x10
+ field public static final int AUDIO_QUALITY_GSM_EFR = 8; // 0x8
+ field public static final int AUDIO_QUALITY_GSM_FR = 9; // 0x9
+ field public static final int AUDIO_QUALITY_GSM_HR = 10; // 0xa
+ field public static final int AUDIO_QUALITY_NONE = 0; // 0x0
+ field public static final int AUDIO_QUALITY_QCELP13K = 3; // 0x3
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsStreamMediaProfile> CREATOR;
+ field public static final int DIRECTION_INACTIVE = 0; // 0x0
+ field public static final int DIRECTION_INVALID = -1; // 0xffffffff
+ field public static final int DIRECTION_RECEIVE = 1; // 0x1
+ field public static final int DIRECTION_SEND = 2; // 0x2
+ field public static final int DIRECTION_SEND_RECEIVE = 3; // 0x3
+ field public static final int RTT_MODE_DISABLED = 0; // 0x0
+ field public static final int RTT_MODE_FULL = 1; // 0x1
+ field public static final int VIDEO_QUALITY_NONE = 0; // 0x0
+ field public static final int VIDEO_QUALITY_QCIF = 1; // 0x1
+ field public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = 2; // 0x2
+ field public static final int VIDEO_QUALITY_QVGA_PORTRAIT = 4; // 0x4
+ field public static final int VIDEO_QUALITY_VGA_LANDSCAPE = 8; // 0x8
+ field public static final int VIDEO_QUALITY_VGA_PORTRAIT = 16; // 0x10
+ }
+
+ public final class ImsSuppServiceNotification implements android.os.Parcelable {
+ ctor public ImsSuppServiceNotification(int, int, int, int, String, String[]);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSuppServiceNotification> CREATOR;
+ field public final int code;
+ field public final String[] history;
+ field public final int index;
+ field public final int notificationType;
+ field public final String number;
+ field public final int type;
+ }
+
+ public class ImsUtListener {
+ method public void onLineIdentificationSupplementaryServiceResponse(int, @NonNull android.telephony.ims.ImsSsInfo);
+ method public void onSupplementaryServiceIndication(android.telephony.ims.ImsSsData);
+ method public void onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]);
+ method public void onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]);
+ method public void onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]);
+ method @Deprecated public void onUtConfigurationQueried(int, android.os.Bundle);
+ method public void onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo);
+ method public void onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo);
+ method public void onUtConfigurationUpdated(int);
+ field @Deprecated public static final String BUNDLE_KEY_CLIR = "queryClir";
+ field @Deprecated public static final String BUNDLE_KEY_SSINFO = "imsSsInfo";
+ }
+
+ public abstract class ImsVideoCallProvider {
+ ctor public ImsVideoCallProvider();
+ method public void changeCallDataUsage(long);
+ method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
+ method public void changePeerDimensions(int, int);
+ method public void changeVideoQuality(int);
+ method public void handleCallSessionEvent(int);
+ method public abstract void onRequestCallDataUsage();
+ method public abstract void onRequestCameraCapabilities();
+ method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile);
+ method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
+ method public abstract void onSetCamera(String);
+ method public void onSetCamera(String, int);
+ method public abstract void onSetDeviceOrientation(int);
+ method public abstract void onSetDisplaySurface(android.view.Surface);
+ method public abstract void onSetPauseImage(android.net.Uri);
+ method public abstract void onSetPreviewSurface(android.view.Surface);
+ method public abstract void onSetZoom(float);
+ method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
+ method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+ }
+
+ public class ProvisioningManager {
+ method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+ field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
+ field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
+ field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
+ field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
+ field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
+ field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
+ }
+
+ public static class ProvisioningManager.Callback {
+ ctor public ProvisioningManager.Callback();
+ method public void onProvisioningIntChanged(int, int);
+ method public void onProvisioningStringChanged(int, @NonNull String);
+ }
+
+ public class RcsUceAdapter {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+ }
+
+}
+
+package android.telephony.ims.feature {
+
+ public final class CapabilityChangeRequest implements android.os.Parcelable {
+ method public void addCapabilitiesToDisableForTech(int, int);
+ method public void addCapabilitiesToEnableForTech(int, int);
+ method public int describeContents();
+ method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToDisable();
+ method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToEnable();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.feature.CapabilityChangeRequest> CREATOR;
+ }
+
+ public static class CapabilityChangeRequest.CapabilityPair {
+ ctor public CapabilityChangeRequest.CapabilityPair(int, int);
+ method public int getCapability();
+ method public int getRadioTech();
+ }
+
+ public abstract class ImsFeature {
+ ctor public ImsFeature();
+ method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public int getFeatureState();
+ method public final int getSlotIndex();
+ method public abstract void onFeatureReady();
+ method public abstract void onFeatureRemoved();
+ method public final void setFeatureState(int);
+ field public static final int CAPABILITY_ERROR_GENERIC = -1; // 0xffffffff
+ field public static final int CAPABILITY_SUCCESS = 0; // 0x0
+ field public static final int FEATURE_EMERGENCY_MMTEL = 0; // 0x0
+ field public static final int FEATURE_MMTEL = 1; // 0x1
+ field public static final int FEATURE_RCS = 2; // 0x2
+ field public static final int STATE_INITIALIZING = 1; // 0x1
+ field public static final int STATE_READY = 2; // 0x2
+ field public static final int STATE_UNAVAILABLE = 0; // 0x0
+ }
+
+ @Deprecated public static class ImsFeature.Capabilities {
+ field @Deprecated protected int mCapabilities;
+ }
+
+ protected static class ImsFeature.CapabilityCallbackProxy {
+ method public void onChangeCapabilityConfigurationError(int, int, int);
+ }
+
+ public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
+ ctor public MmTelFeature();
+ method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
+ method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile);
+ method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
+ method @NonNull public android.telephony.ims.stub.ImsMultiEndpointImplBase getMultiEndpoint();
+ method @NonNull public android.telephony.ims.stub.ImsSmsImplBase getSmsImplementation();
+ method @NonNull public android.telephony.ims.stub.ImsUtImplBase getUt();
+ method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
+ method public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle);
+ method public final void notifyRejectedCall(@NonNull android.telephony.ims.ImsCallProfile, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public final void notifyVoiceMessageCountUpdate(int);
+ method public void onFeatureReady();
+ method public void onFeatureRemoved();
+ method public boolean queryCapabilityConfiguration(int, int);
+ method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
+ method public void setUiTtyMode(int, @Nullable android.os.Message);
+ method public int shouldProcessCall(@NonNull String[]);
+ field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
+ field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
+ field public static final int PROCESS_CALL_CSFB = 1; // 0x1
+ field public static final int PROCESS_CALL_IMS = 0; // 0x0
+ }
+
+ public static class MmTelFeature.MmTelCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+ ctor public MmTelFeature.MmTelCapabilities();
+ ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
+ ctor public MmTelFeature.MmTelCapabilities(int);
+ method public final void addCapabilities(int);
+ method public final boolean isCapable(int);
+ method public final void removeCapabilities(int);
+ }
+
+ public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
+ ctor public RcsFeature();
+ method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method public void onFeatureReady();
+ method public void onFeatureRemoved();
+ }
+
+}
+
+package android.telephony.ims.stub {
+
+ public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
+ ctor public ImsCallSessionImplBase();
+ method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
+ method public void close();
+ method public void deflect(String);
+ method public void extendToConference(String[]);
+ method public String getCallId();
+ method public android.telephony.ims.ImsCallProfile getCallProfile();
+ method public android.telephony.ims.ImsVideoCallProvider getImsVideoCallProvider();
+ method public android.telephony.ims.ImsCallProfile getLocalCallProfile();
+ method public String getProperty(String);
+ method public android.telephony.ims.ImsCallProfile getRemoteCallProfile();
+ method public int getState();
+ method public void hold(android.telephony.ims.ImsStreamMediaProfile);
+ method public void inviteParticipants(String[]);
+ method public boolean isInCall();
+ method public boolean isMultiparty();
+ method public void merge();
+ method public void reject(int);
+ method public void removeParticipants(String[]);
+ method public void resume(android.telephony.ims.ImsStreamMediaProfile);
+ method public void sendDtmf(char, android.os.Message);
+ method public void sendRttMessage(String);
+ method public void sendRttModifyRequest(android.telephony.ims.ImsCallProfile);
+ method public void sendRttModifyResponse(boolean);
+ method public void sendUssd(String);
+ method public void setListener(android.telephony.ims.ImsCallSessionListener);
+ method public void setMute(boolean);
+ method public void start(String, android.telephony.ims.ImsCallProfile);
+ method public void startConference(String[], android.telephony.ims.ImsCallProfile);
+ method public void startDtmf(char);
+ method public void stopDtmf();
+ method public void terminate(int);
+ method public void update(int, android.telephony.ims.ImsStreamMediaProfile);
+ field public static final int USSD_MODE_NOTIFY = 0; // 0x0
+ field public static final int USSD_MODE_REQUEST = 1; // 0x1
+ }
+
+ public static class ImsCallSessionImplBase.State {
+ method public static String toString(int);
+ field public static final int ESTABLISHED = 4; // 0x4
+ field public static final int ESTABLISHING = 3; // 0x3
+ field public static final int IDLE = 0; // 0x0
+ field public static final int INITIATED = 1; // 0x1
+ field public static final int INVALID = -1; // 0xffffffff
+ field public static final int NEGOTIATING = 2; // 0x2
+ field public static final int REESTABLISHING = 6; // 0x6
+ field public static final int RENEGOTIATING = 5; // 0x5
+ field public static final int TERMINATED = 8; // 0x8
+ field public static final int TERMINATING = 7; // 0x7
+ }
+
+ public class ImsConfigImplBase {
+ ctor public ImsConfigImplBase();
+ method public int getConfigInt(int);
+ method public String getConfigString(int);
+ method public final void notifyProvisionedValueChanged(int, int);
+ method public final void notifyProvisionedValueChanged(int, String);
+ method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
+ method public int setConfig(int, int);
+ method public int setConfig(int, String);
+ field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
+ field public static final int CONFIG_RESULT_SUCCESS = 0; // 0x0
+ field public static final int CONFIG_RESULT_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public class ImsEcbmImplBase {
+ ctor public ImsEcbmImplBase();
+ method public final void enteredEcbm();
+ method public void exitEmergencyCallbackMode();
+ method public final void exitedEcbm();
+ }
+
+ public final class ImsFeatureConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.Set<android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair> getServiceFeatures();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.stub.ImsFeatureConfiguration> CREATOR;
+ }
+
+ public static class ImsFeatureConfiguration.Builder {
+ ctor public ImsFeatureConfiguration.Builder();
+ method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int, int);
+ method public android.telephony.ims.stub.ImsFeatureConfiguration build();
+ }
+
+ public static final class ImsFeatureConfiguration.FeatureSlotPair {
+ ctor public ImsFeatureConfiguration.FeatureSlotPair(int, int);
+ field public final int featureType;
+ field public final int slotId;
+ }
+
+ public class ImsMultiEndpointImplBase {
+ ctor public ImsMultiEndpointImplBase();
+ method public final void onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>);
+ method public void requestImsExternalCallStateInfo();
+ }
+
+ public class ImsRegistrationImplBase {
+ ctor public ImsRegistrationImplBase();
+ method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
+ method public final void onRegistered(int);
+ method public final void onRegistering(int);
+ method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
+ method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
+ field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
+ field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
+ field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
+ }
+
+ public class ImsSmsImplBase {
+ ctor public ImsSmsImplBase();
+ method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int);
+ method public void acknowledgeSmsReport(int, @IntRange(from=0, to=65535) int, int);
+ method public String getSmsFormat();
+ method public void onReady();
+ method @Deprecated public final void onSendSmsResult(int, @IntRange(from=0, to=65535) int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultError(int, @IntRange(from=0, to=65535) int, int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultSuccess(int, @IntRange(from=0, to=65535) int) throws java.lang.RuntimeException;
+ method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException;
+ method @Deprecated public final void onSmsStatusReportReceived(int, @IntRange(from=0, to=65535) int, String, byte[]) throws java.lang.RuntimeException;
+ method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException;
+ method public void sendSms(int, @IntRange(from=0, to=65535) int, String, String, boolean, byte[]);
+ field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2
+ field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3
+ field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4
+ field public static final int DELIVER_STATUS_OK = 1; // 0x1
+ field public static final int RESULT_NO_NETWORK_ERROR = -1; // 0xffffffff
+ field public static final int SEND_STATUS_ERROR = 2; // 0x2
+ field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4
+ field public static final int SEND_STATUS_ERROR_RETRY = 3; // 0x3
+ field public static final int SEND_STATUS_OK = 1; // 0x1
+ field public static final int STATUS_REPORT_STATUS_ERROR = 2; // 0x2
+ field public static final int STATUS_REPORT_STATUS_OK = 1; // 0x1
+ }
+
+ public class ImsUtImplBase {
+ ctor public ImsUtImplBase();
+ method public void close();
+ method public int queryCallBarring(int);
+ method public int queryCallBarringForServiceClass(int, int);
+ method public int queryCallForward(int, String);
+ method public int queryCallWaiting();
+ method public int queryClip();
+ method public int queryClir();
+ method public int queryColp();
+ method public int queryColr();
+ method public void setListener(android.telephony.ims.ImsUtListener);
+ method public int transact(android.os.Bundle);
+ method public int updateCallBarring(int, int, String[]);
+ method public int updateCallBarringForServiceClass(int, int, String[], int);
+ method public int updateCallForward(int, int, String, int, int);
+ method public int updateCallWaiting(boolean, int);
+ method public int updateClip(boolean);
+ method public int updateClir(int);
+ method public int updateColp(boolean);
+ method public int updateColr(int);
+ }
+
+}
+
+package android.telephony.mbms {
+
+ public static class DownloadRequest.Builder {
+ method public android.telephony.mbms.DownloadRequest.Builder setServiceId(String);
+ }
+
+ public final class FileInfo implements android.os.Parcelable {
+ ctor public FileInfo(android.net.Uri, String);
+ }
+
+ public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
+ ctor public FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>);
+ }
+
+ public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
+ field public static final int RESULT_APP_NOTIFICATION_ERROR = 6; // 0x6
+ field public static final int RESULT_BAD_TEMP_FILE_ROOT = 3; // 0x3
+ field public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4; // 0x4
+ field public static final int RESULT_INVALID_ACTION = 1; // 0x1
+ field public static final int RESULT_MALFORMED_INTENT = 2; // 0x2
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5; // 0x5
+ }
+
+ public final class StreamingServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
+ ctor public StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date);
+ }
+
+ public final class UriPathPair implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.net.Uri getContentUri();
+ method public android.net.Uri getFilePathUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.mbms.UriPathPair> CREATOR;
+ }
+
+}
+
+package android.telephony.mbms.vendor {
+
+ public class MbmsDownloadServiceBase extends android.os.Binder implements android.os.IInterface {
+ ctor public MbmsDownloadServiceBase();
+ method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+ method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
+ method public android.os.IBinder asBinder();
+ method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public void dispose(int) throws android.os.RemoteException;
+ method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
+ method @NonNull public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+ method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+ method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
+ method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
+ method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public int setTempFileRootDirectory(int, String) throws android.os.RemoteException;
+ }
+
+ public class MbmsGroupCallServiceBase extends android.app.Service {
+ ctor public MbmsGroupCallServiceBase();
+ method public void dispose(int) throws android.os.RemoteException;
+ method public int initialize(@NonNull android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public int startGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, @NonNull android.telephony.mbms.GroupCallCallback);
+ method public void stopGroupCall(int, long);
+ method public void updateGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>);
+ }
+
+ public class MbmsStreamingServiceBase extends android.os.Binder implements android.os.IInterface {
+ ctor public MbmsStreamingServiceBase();
+ method public android.os.IBinder asBinder();
+ method public void dispose(int) throws android.os.RemoteException;
+ method @Nullable public android.net.Uri getPlaybackUri(int, String) throws android.os.RemoteException;
+ method public int initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+ method public int requestUpdateStreamingServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public int startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback) throws android.os.RemoteException;
+ method public void stopStreaming(int, String) throws android.os.RemoteException;
+ }
+
+ public class VendorUtils {
+ ctor public VendorUtils();
+ method public static android.content.ComponentName getAppReceiverFromPackageName(android.content.Context, String);
+ field public static final String ACTION_CLEANUP = "android.telephony.mbms.action.CLEANUP";
+ field public static final String ACTION_DOWNLOAD_RESULT_INTERNAL = "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+ field public static final String ACTION_FILE_DESCRIPTOR_REQUEST = "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+ field public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+ field public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+ field public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+ field public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+ field public static final String EXTRA_PAUSED_URI_LIST = "android.telephony.mbms.extra.PAUSED_URI_LIST";
+ field public static final String EXTRA_SERVICE_ID = "android.telephony.mbms.extra.SERVICE_ID";
+ field public static final String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+ field public static final String EXTRA_TEMP_FILE_ROOT = "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+ field public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+ }
+
+}
+
diff --git a/telephony/api/system-removed.txt b/telephony/api/system-removed.txt
new file mode 100644
index 0000000..ae46075
--- /dev/null
+++ b/telephony/api/system-removed.txt
@@ -0,0 +1,19 @@
+// Signature format: 2.0
+package android.telephony {
+
+ public class TelephonyManager {
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void answerRingingCall();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public boolean endCall();
+ method @Deprecated public void silenceRinger();
+ }
+
+}
+
+package android.telephony.data {
+
+ public final class DataCallResponse implements android.os.Parcelable {
+ ctor public DataCallResponse(int, int, int, int, int, @Nullable String, @Nullable java.util.List<android.net.LinkAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.net.InetAddress>, int);
+ }
+
+}
+
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index d9ae48f..b3d7c0d 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -30,6 +30,7 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.ArrayUtils;
@@ -162,7 +163,7 @@
for (ApplicationInfo ai : candidates) {
String packageName = ai.packageName;
String[] restrictedCarrierApps = Resources.getSystem().getStringArray(
- android.R.array.config_restrictedPreinstalledCarrierApps);
+ R.array.config_restrictedPreinstalledCarrierApps);
boolean hasPrivileges = telephonyManager != null
&& telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
index c62cec2..5c53f7e 100644
--- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
@@ -19,10 +19,12 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.os.Build;
-import android.text.TextUtils;
import android.util.Log;
+import android.text.TextUtils;
import android.util.SparseIntArray;
+import com.android.internal.R;
+
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -1087,10 +1089,8 @@
private static void enableCountrySpecificEncodings() {
Resources r = Resources.getSystem();
// See comments in frameworks/base/core/res/res/values/config.xml for allowed values
- sEnabledSingleShiftTables = r.getIntArray(
- android.R.array.config_sms_enabled_single_shift_tables);
- sEnabledLockingShiftTables = r.getIntArray(
- android.R.array.config_sms_enabled_locking_shift_tables);
+ sEnabledSingleShiftTables = r.getIntArray(R.array.config_sms_enabled_single_shift_tables);
+ sEnabledLockingShiftTables = r.getIntArray(R.array.config_sms_enabled_locking_shift_tables);
if (sEnabledSingleShiftTables.length > 0) {
sHighestEnabledSingleShiftCode =
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index b897725..f0f9721 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -437,8 +437,9 @@
/**
* Returns whether the caller can read phone numbers.
*
- * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
- * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
+ * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}
+ * (only prior to R), the default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS
+ * can also read phone numbers.
*/
public static boolean checkCallingOrSelfReadPhoneNumber(
Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
@@ -451,8 +452,9 @@
/**
* Returns whether the caller can read phone numbers.
*
- * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
- * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
+ * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}
+ * (only prior to R), the default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS
+ * can also read phone numbers.
*/
@VisibleForTesting
public static boolean checkReadPhoneNumber(
@@ -468,12 +470,15 @@
// NOTE(b/73308711): If an app has one of the following AppOps bits explicitly revoked, they
// will be denied access, even if they have another permission and AppOps bit if needed.
- // First, check if we can read the phone state.
+ // First, check if we can read the phone state and the SDK version is below R.
try {
- return checkReadPhoneState(
- context, subId, pid, uid, callingPackage, callingFeatureId,
- message);
- } catch (SecurityException readPhoneStateSecurityException) {
+ ApplicationInfo info = context.getPackageManager().getApplicationInfoAsUser(
+ callingPackage, 0, UserHandle.getUserHandleForUid(Binder.getCallingUid()));
+ if (info.targetSdkVersion <= Build.VERSION_CODES.Q) {
+ return checkReadPhoneState(
+ context, subId, pid, uid, callingPackage, callingFeatureId, message);
+ }
+ } catch (SecurityException | PackageManager.NameNotFoundException e) {
}
// Can be read with READ_SMS too.
try {
diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
index 4871434..31fe4d7 100644
--- a/telephony/common/com/google/android/mms/util/SqliteWrapper.java
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
@@ -60,7 +60,8 @@
@UnsupportedAppUsage
public static void checkSQLiteException(Context context, SQLiteException e) {
if (isLowMemory(e)) {
- Toast.makeText(context, android.R.string.low_memory, Toast.LENGTH_SHORT).show();
+ Toast.makeText(context, com.android.internal.R.string.low_memory,
+ Toast.LENGTH_SHORT).show();
} else {
throw e;
}
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index dcd4eb5..b8a74d2 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -571,6 +571,19 @@
public @interface PreciseDisconnectCauses {
}
+ /**
+ * Carrier Privilege Status.
+ */
+ @IntDef(prefix = { "CARRIER_PRIVILEGE_STATUS_" }, value = {
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS,
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS,
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED,
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CarrierPrivilegeStatus {
+ }
+
@IntDef({
Connection.AUDIO_CODEC_NONE,
Connection.AUDIO_CODEC_AMR,
@@ -598,48 +611,6 @@
}
/**
- * Call forwarding function status
- */
- @IntDef(prefix = { "STATUS_" }, value = {
- CallForwardingInfo.STATUS_ACTIVE,
- CallForwardingInfo.STATUS_INACTIVE,
- CallForwardingInfo.STATUS_UNKNOWN_ERROR,
- CallForwardingInfo.STATUS_NOT_SUPPORTED,
- CallForwardingInfo.STATUS_FDN_CHECK_FAILURE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallForwardingStatus {
- }
-
- /**
- * Call forwarding reason types
- */
- @IntDef(flag = true, prefix = { "REASON_" }, value = {
- CallForwardingInfo.REASON_UNCONDITIONAL,
- CallForwardingInfo.REASON_BUSY,
- CallForwardingInfo.REASON_NO_REPLY,
- CallForwardingInfo.REASON_NOT_REACHABLE,
- CallForwardingInfo.REASON_ALL,
- CallForwardingInfo.REASON_ALL_CONDITIONAL
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallForwardingReason {
- }
-
- /**
- * Call waiting function status
- */
- @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
- TelephonyManager.CALL_WAITING_STATUS_ACTIVE,
- TelephonyManager.CALL_WAITING_STATUS_INACTIVE,
- TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED,
- TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallWaitingStatus {
- }
-
- /**
* UICC SIM Application Types
*/
@IntDef(prefix = { "APPTYPE_" }, value = {
diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java
index 1dd7539..7e777fa 100644
--- a/telephony/java/android/telephony/CallForwardingInfo.java
+++ b/telephony/java/android/telephony/CallForwardingInfo.java
@@ -15,24 +15,24 @@
*/
package android.telephony;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Annotation.CallForwardingReason;
-import android.telephony.Annotation.CallForwardingStatus;
import com.android.telephony.Rlog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Defines the call forwarding information.
* @hide
*/
-@SystemApi
public final class CallForwardingInfo implements Parcelable {
private static final String TAG = "CallForwardingInfo";
@@ -41,7 +41,6 @@
*
* @hide
*/
- @SystemApi
public static final int STATUS_INACTIVE = 0;
/**
@@ -49,7 +48,6 @@
*
* @hide
*/
- @SystemApi
public static final int STATUS_ACTIVE = 1;
/**
@@ -58,7 +56,6 @@
*
* @hide
*/
- @SystemApi
public static final int STATUS_FDN_CHECK_FAILURE = 2;
/**
@@ -66,7 +63,6 @@
*
* @hide
*/
- @SystemApi
public static final int STATUS_UNKNOWN_ERROR = 3;
/**
@@ -74,7 +70,6 @@
*
* @hide
*/
- @SystemApi
public static final int STATUS_NOT_SUPPORTED = 4;
/**
@@ -83,7 +78,6 @@
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_UNCONDITIONAL = 0;
/**
@@ -92,7 +86,6 @@
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_BUSY = 1;
/**
@@ -101,7 +94,6 @@
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_NO_REPLY = 2;
/**
@@ -110,7 +102,6 @@
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_NOT_REACHABLE = 3;
/**
@@ -120,7 +111,6 @@
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_ALL = 4;
/**
@@ -130,20 +120,48 @@
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_ALL_CONDITIONAL = 5;
/**
+ * Call forwarding function status
+ */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_ACTIVE,
+ STATUS_INACTIVE,
+ STATUS_UNKNOWN_ERROR,
+ STATUS_NOT_SUPPORTED,
+ STATUS_FDN_CHECK_FAILURE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallForwardingStatus {
+ }
+
+ /**
+ * Call forwarding reason types
+ */
+ @IntDef(flag = true, prefix = { "REASON_" }, value = {
+ REASON_UNCONDITIONAL,
+ REASON_BUSY,
+ REASON_NO_REPLY,
+ REASON_NOT_REACHABLE,
+ REASON_ALL,
+ REASON_ALL_CONDITIONAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallForwardingReason {
+ }
+
+ /**
* The call forwarding status.
*/
- private @CallForwardingStatus int mStatus;
+ private int mStatus;
/**
* The call forwarding reason indicates the condition under which calls will be forwarded.
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
* and conditions +CCFC
*/
- private @CallForwardingReason int mReason;
+ private int mReason;
/**
* The phone number to which calls will be forwarded.
@@ -166,7 +184,6 @@
* @param timeSeconds the timeout (in seconds) before the forwarding is attempted
* @hide
*/
- @SystemApi
public CallForwardingInfo(@CallForwardingStatus int status, @CallForwardingReason int reason,
@Nullable String number, int timeSeconds) {
mStatus = status;
@@ -182,7 +199,6 @@
*
* @hide
*/
- @SystemApi
public @CallForwardingStatus int getStatus() {
return mStatus;
}
@@ -196,7 +212,6 @@
*
* @hide
*/
- @SystemApi
public @CallForwardingReason int getReason() {
return mReason;
}
@@ -209,7 +224,6 @@
*
* @hide
*/
- @SystemApi
@Nullable
public String getNumber() {
return mNumber;
@@ -227,7 +241,6 @@
*
* @hide
*/
- @SystemApi
@SuppressLint("MethodNameUnits")
public int getTimeoutSeconds() {
return mTimeSeconds;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c20748b..e2112a5 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2382,17 +2382,16 @@
* <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
* not be used for calculating signal level. If multiple measures are set bit, the parameter
* whose value is smallest is used to indicate the signal level.
+ * <UL>
+ * <LI>RSRP = 1 << 0</LI>
+ * <LI>RSRQ = 1 << 1</LI>
+ * <LI>RSSNR = 1 << 2</LI>
+ * </UL>
+ * <p> The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP},
+ * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
*
- * RSRP = 1 << 0,
- * RSRQ = 1 << 1,
- * RSSNR = 1 << 2,
- *
- * The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP},
- * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
- *
- * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
- * If the key is invalid or not configured, a default value (RSRP = 1 << 0)
- * will apply.
+ * <p> For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+ * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply.
*
* @hide
*/
@@ -2401,16 +2400,18 @@
/**
* List of 4 customized 5G SS reference signal received power (SSRSRP) thresholds.
- *
+ * <p>
* Reference: 3GPP TS 38.215
- *
+ * <p>
* 4 threshold integers must be within the boundaries [-140 dB, -44 dB], and the levels are:
- * "NONE: [-140, threshold1]"
- * "POOR: (threshold1, threshold2]"
- * "MODERATE: (threshold2, threshold3]"
- * "GOOD: (threshold3, threshold4]"
- * "EXCELLENT: (threshold4, -44]"
- *
+ * <UL>
+ * <LI>"NONE: [-140, threshold1]"</LI>
+ * <LI>"POOR: (threshold1, threshold2]"</LI>
+ * <LI>"MODERATE: (threshold2, threshold3]"</LI>
+ * <LI>"GOOD: (threshold3, threshold4]"</LI>
+ * <LI>"EXCELLENT: (threshold4, -44]"</LI>
+ * </UL>
+ * <p>
* This key is considered invalid if the format is violated. If the key is invalid or
* not configured, a default value set will apply.
*/
@@ -2419,16 +2420,18 @@
/**
* List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds.
- *
+ * <p>
* Reference: 3GPP TS 38.215
- *
+ * <p>
* 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are:
- * "NONE: [-20, threshold1]"
- * "POOR: (threshold1, threshold2]"
- * "MODERATE: (threshold2, threshold3]"
- * "GOOD: (threshold3, threshold4]"
- * "EXCELLENT: (threshold4, -3]"
- *
+ * <UL>
+ * <LI>"NONE: [-20, threshold1]"</LI>
+ * <LI>"POOR: (threshold1, threshold2]"</LI>
+ * <LI>"MODERATE: (threshold2, threshold3]"</LI>
+ * <LI>"GOOD: (threshold3, threshold4]"</LI>
+ * <LI>"EXCELLENT: (threshold4, -3]"</LI>
+ * </UL>
+ * <p>
* This key is considered invalid if the format is violated. If the key is invalid or
* not configured, a default value set will apply.
*/
@@ -2437,17 +2440,19 @@
/**
* List of 4 customized 5G SS signal-to-noise and interference ratio (SSSINR) thresholds.
- *
+ * <p>
* Reference: 3GPP TS 38.215,
* 3GPP TS 38.133 10.1.16.1
- *
+ * <p>
* 4 threshold integers must be within the boundaries [-23 dB, 40 dB], and the levels are:
- * "NONE: [-23, threshold1]"
- * "POOR: (threshold1, threshold2]"
- * "MODERATE: (threshold2, threshold3]"
- * "GOOD: (threshold3, threshold4]"
- * "EXCELLENT: (threshold4, 40]"
- *
+ * <UL>
+ * <LI>"NONE: [-23, threshold1]"</LI>
+ * <LI>"POOR: (threshold1, threshold2]"</LI>
+ * <LI>"MODERATE: (threshold2, threshold3]"</LI>
+ * <LI>"GOOD: (threshold3, threshold4]"</LI>
+ * <LI>"EXCELLENT: (threshold4, 40]"</LI>
+ * </UL>
+ * <p>
* This key is considered invalid if the format is violated. If the key is invalid or
* not configured, a default value set will apply.
*/
@@ -2462,19 +2467,19 @@
* <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
* not be used for calculating signal level. If multiple measures are set bit, the parameter
* whose value is smallest is used to indicate the signal level.
- *
- * SSRSRP = 1 << 0,
- * SSRSRQ = 1 << 1,
- * SSSINR = 1 << 2,
- *
+ * <UL>
+ * <LI>SSRSRP = 1 << 0</LI>
+ * <LI>SSRSRQ = 1 << 1</LI>
+ * <LI>SSSINR = 1 << 2</LI>
+ * </UL>
* The value of this key must be bitwise OR of {@link CellSignalStrengthNr#USE_SSRSRP},
* {@link CellSignalStrengthNr#USE_SSRSRQ}, {@link CellSignalStrengthNr#USE_SSSINR}.
*
- * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2).
+ * <p> For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2).
* If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply.
*
- * Reference: 3GPP TS 38.215,
- * 3GPP TS 38.133 10.1.16.1
+ * <p> Reference: 3GPP TS 38.215,
+ * 3GPP TS 38.133 10.1.16.1
*
* @hide
*/
@@ -4157,7 +4162,7 @@
return null;
}
return loader.getConfigForSubIdWithFeature(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
Rlog.e(TAG, "Error getting config for subId " + subId + ": "
+ ex.toString());
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 34bac5d..3984bd7 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -19,9 +19,7 @@
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.content.Context;
import android.telephony.SubscriptionManager;
@@ -44,8 +42,6 @@
* issues.
* @hide
*/
- @SystemApi
- @TestApi
// Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have
// this value hard-coded in BroadcastReceiver.
@SuppressLint("ActionValue")
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 1a79bf7..4940cb2 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -297,7 +297,7 @@
mDataSpecificInfo = new DataSpecificRegistrationInfo(
maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo,
isUsingCarrierAggregation);
- updateNrState(mDataSpecificInfo);
+ updateNrState();
}
private NetworkRegistrationInfo(Parcel source) {
@@ -686,12 +686,12 @@
* DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
* NR is restricted.
*
- * @param state data specific registration state contains the 5G NR indicators.
+ * @hide
*/
- private void updateNrState(DataSpecificRegistrationInfo state) {
+ public void updateNrState() {
mNrState = NR_STATE_NONE;
- if (state.isEnDcAvailable) {
- if (!state.isDcNrRestricted && state.isNrAvailable) {
+ if (mDataSpecificInfo.isEnDcAvailable) {
+ if (!mDataSpecificInfo.isDcNrRestricted && mDataSpecificInfo.isNrAvailable) {
mNrState = NR_STATE_NOT_RESTRICTED;
} else {
mNrState = NR_STATE_RESTRICTED;
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 87d94bfd..c75de42 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -234,6 +234,9 @@
* this method to facilitate the creation of {@link NetworkServiceProvider} instances. The system
* will call this method after binding the network service for each active SIM slot id.
*
+ * This methead is guaranteed to be invoked in {@link NetworkService}'s internal handler thread
+ * whose looper can be retrieved with {@link Looper.myLooper()} when override this method.
+ *
* @param slotIndex SIM slot id the network service associated with.
* @return Network service object. Null if failed to create the provider (e.g. invalid slot
* index)
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index e37a9b9..c2cfbef 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -95,7 +95,6 @@
* if there is no valid APN setting for the specific type, then this will be null
* @hide
*/
- @SystemApi
public PreciseDataConnectionState(@DataState int state,
@NetworkType int networkType,
@ApnType int apnTypes, @NonNull String apn,
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 45cba51..deba551 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -580,7 +580,6 @@
*
* @hide
*/
- @SystemApi
public @RegState int getDataRegistrationState() {
return getDataRegState();
}
@@ -1651,7 +1650,6 @@
* @return Current data network type
* @hide
*/
- @SystemApi
@TestApi
public @NetworkType int getDataNetworkType() {
final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index d3afa4a..6aea5ee 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -975,11 +975,7 @@
*
* @param packageName serves as the default package name if the package name that is
* associated with the user id is null.
- *
- * @hide
*/
- @SystemApi
- @TestApi
public void sendMultipartTextMessage(
@NonNull String destinationAddress, @Nullable String scAddress,
@NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
@@ -2898,7 +2894,7 @@
getSubscriptionId(), null);
}
} catch (RemoteException ex) {
- // ignore it
+ throw new RuntimeException(ex);
}
return smsc;
}
@@ -2920,7 +2916,7 @@
* </p>
*
* @param smsc the SMSC address string.
- * @return true for success, false otherwise.
+ * @return true for success, false otherwise. Failure can be due modem returning an error.
*/
@SuppressAutoDoc // for carrier privileges and default SMS application.
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -2932,7 +2928,7 @@
smsc, getSubscriptionId(), null);
}
} catch (RemoteException ex) {
- // ignore it
+ throw new RuntimeException(ex);
}
return false;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8ac9023b..01a40f5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1144,7 +1144,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -1178,7 +1178,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -1212,7 +1212,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -1236,7 +1236,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -1300,8 +1300,13 @@
* both active and hidden SubscriptionInfos.
*
*/
- public @Nullable List<SubscriptionInfo> getActiveAndHiddenSubscriptionInfoList() {
- return getActiveSubscriptionInfoList(/* userVisibleonly */false);
+ public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() {
+ List<SubscriptionInfo> completeList = getActiveSubscriptionInfoList(
+ /* userVisibleonly */false);
+ if (completeList == null) {
+ completeList = new ArrayList<>();
+ }
+ return completeList;
}
/**
@@ -1317,7 +1322,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -1368,7 +1373,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -1486,7 +1491,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getAllSubInfoCount(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -1515,7 +1520,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -2270,7 +2275,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
resultValue = iSub.getSubscriptionProperty(subId, propKey,
- context.getOpPackageName(), context.getFeatureId());
+ context.getOpPackageName(), context.getAttributionTag());
}
} catch (RemoteException ex) {
// ignore it
@@ -2434,7 +2439,7 @@
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
}
@@ -2697,13 +2702,14 @@
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions() {
String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>";
- String contextFeature = mContext != null ? mContext.getFeatureId() : null;
+ String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null;
List<SubscriptionInfo> subInfoList = null;
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature);
+ subInfoList = iSub.getOpportunisticSubscriptions(contextPkg,
+ contextAttributionTag);
}
} catch (RemoteException ex) {
// ignore it
@@ -2942,7 +2948,7 @@
public @NonNull List<SubscriptionInfo> getSubscriptionsInGroup(@NonNull ParcelUuid groupUuid) {
Preconditions.checkNotNull(groupUuid, "groupUuid can't be null");
String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>";
- String contextFeature = mContext != null ? mContext.getFeatureId() : null;
+ String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null;
if (VDBG) {
logd("[getSubscriptionsInGroup]+ groupUuid:" + groupUuid);
}
@@ -2951,7 +2957,8 @@
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature);
+ result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg,
+ contextAttributionTag);
} else {
if (!isSystemProcess()) {
throw new IllegalStateException("telephony service is null.");
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 631eaac..fbd69da 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -70,13 +70,13 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.CallForwardingReason;
import android.telephony.Annotation.CallState;
-import android.telephony.Annotation.CallWaitingStatus;
+import android.telephony.Annotation.CarrierPrivilegeStatus;
import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.UiccAppType;
+import android.telephony.CallForwardingInfo.CallForwardingReason;
import android.telephony.VisualVoicemailService.VisualVoicemailTask;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.MvnoType;
@@ -345,10 +345,10 @@
mSubId = subId;
Context appContext = context.getApplicationContext();
if (appContext != null) {
- if (Objects.equals(context.getFeatureId(), appContext.getFeatureId())) {
+ if (Objects.equals(context.getAttributionTag(), appContext.getAttributionTag())) {
mContext = appContext;
} else {
- mContext = appContext.createFeatureContext(context.getFeatureId());
+ mContext = appContext.createAttributionContext(context.getAttributionTag());
}
} else {
mContext = context;
@@ -393,12 +393,12 @@
}
}
- private String getFeatureId() {
+ private String getAttributionTag() {
// For legacy reasons the TelephonyManager has API for getting
// a static instance with no context set preventing us from
- // getting the feature Id.
+ // getting the attribution tag.
if (mContext != null) {
- return mContext.getFeatureId();
+ return mContext.getAttributionTag();
}
return null;
}
@@ -1565,6 +1565,7 @@
* @hide
*/
@SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@SuppressLint("ActionValue")
public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED =
"android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
@@ -1786,6 +1787,7 @@
* @hide
*/
@SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@SuppressLint("ActionValue")
public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED =
"android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
@@ -1896,7 +1898,7 @@
try {
return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1938,7 +1940,7 @@
if (telephony == null)
return null;
return telephony.getDeviceIdWithFeature(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1983,7 +1985,7 @@
if (info == null)
return null;
return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -2041,7 +2043,7 @@
if (telephony == null) return null;
try {
- return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getFeatureId());
+ return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -2135,7 +2137,8 @@
if (telephony == null) return null;
try {
- String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(), getFeatureId());
+ String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(),
+ getAttributionTag());
if (TextUtils.isEmpty(meid)) {
Log.d(TAG, "getMeid: return null because MEID is not available");
return null;
@@ -2237,7 +2240,7 @@
if (info == null)
return null;
String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Rlog.v(TAG, "Nai = " + nai);
}
@@ -2271,7 +2274,7 @@
}
CellIdentity cellIdentity = telephony.getCellLocation(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
CellLocation cl = cellIdentity.asCellLocation();
if (cl == null || cl.isEmpty()) {
Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty or"
@@ -2355,7 +2358,7 @@
if (telephony == null)
return null;
return telephony.getNeighboringCellInfo(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -2371,7 +2374,12 @@
public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA;
/** Phone is via SIP. */
public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP;
- /** Phone is via IMS. */
+
+ /**
+ * Phone is via IMS.
+ *
+ * @hide
+ */
public static final int PHONE_TYPE_IMS = PhoneConstants.PHONE_TYPE_IMS;
/**
@@ -2379,7 +2387,6 @@
*
* @hide
*/
- @SystemApi
public static final int PHONE_TYPE_THIRD_PARTY = PhoneConstants.PHONE_TYPE_THIRD_PARTY;
/**
@@ -2957,7 +2964,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} else {
// This can happen when the ITelephony interface is not up yet.
return NETWORK_TYPE_UNKNOWN;
@@ -3022,7 +3029,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} else {
// This can happen when the ITelephony interface is not up yet.
return NETWORK_TYPE_UNKNOWN;
@@ -3059,7 +3066,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getVoiceNetworkTypeForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} else {
// This can happen when the ITelephony interface is not up yet.
return NETWORK_TYPE_UNKNOWN;
@@ -3763,29 +3770,6 @@
}
/**
- * Returns the ISO-3166 country code equivalent for the SIM provider's country code
- * of the default subscription
- * <p>
- * The ISO-3166 country code is provided in lowercase 2 character format.
- * @return the lowercase 2 character ISO-3166 country code, or empty string is not available.
- * <p>
- * Note: This API is introduced to unblock mainlining work as the following APIs in
- * Linkify.java invokes getSimCountryIso() without a context. TODO(Bug 144576376): remove
- * this API once the following APIs are redesigned to access telephonymanager with a context.
- *
- * {@link Linkify#addLinks(@NonNull Spannable text, @LinkifyMask int mask)}
- * {@link Linkify#addLinks(@NonNull Spannable text, @LinkifyMask int mask,
- @Nullable Function<String, URLSpan> urlSpanFactory)}
- *
- * @hide
- */
- @SystemApi
- @NonNull
- public static String getDefaultSimCountryIso() {
- return getSimCountryIso(SubscriptionManager.getDefaultSubscriptionId());
- }
-
- /**
* Returns the ISO country code equivalent for the SIM provider's country code.
*
* @param subId for which SimCountryIso is returned
@@ -3868,7 +3852,7 @@
if (info == null)
return null;
return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -3912,7 +3896,7 @@
if (telephony == null)
return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
return telephony.getLteOnCdmaModeForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
// Assume no ICC card if remote exception which shouldn't happen
return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
@@ -4141,7 +4125,7 @@
if (info == null)
return null;
return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4309,7 +4293,7 @@
if (info == null)
return null;
return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4333,7 +4317,7 @@
if (info == null)
return null;
return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4344,14 +4328,18 @@
/**
* Returns the phone number string for line 1, for example, the MSISDN
- * for a GSM phone. Return null if it is unavailable.
+ * for a GSM phone for a particular subscription. Return null if it is unavailable.
+ * <p>
+ * The default SMS app can also use this.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE},
* {@link android.Manifest.permission#READ_SMS READ_SMS},
* {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
* that the caller is the default SMS app,
- * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+ * for any API level.
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * for apps targeting SDK API level 29 and below.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
@RequiresPermission(anyOf = {
@@ -4369,6 +4357,15 @@
* <p>
* The default SMS app can also use this.
*
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_SMS READ_SMS},
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+ * that the caller is the default SMS app,
+ * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+ * for any API level.
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * for apps targeting SDK API level 29 and below.
+ *
* @param subId whose phone number for line 1 is returned
* @hide
*/
@@ -4384,7 +4381,7 @@
ITelephony telephony = getITelephony();
if (telephony != null)
number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
@@ -4396,7 +4393,7 @@
if (info == null)
return null;
return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4475,7 +4472,7 @@
ITelephony telephony = getITelephony();
if (telephony != null)
alphaTag = telephony.getLine1AlphaTagForDisplay(subId,
- getOpPackageName(), getFeatureId());
+ getOpPackageName(), getAttributionTag());
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
@@ -4487,7 +4484,7 @@
if (info == null)
return null;
return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4517,7 +4514,7 @@
ITelephony telephony = getITelephony();
if (telephony != null)
return telephony.getMergedSubscriberIds(getSubId(), getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
@@ -4549,32 +4546,57 @@
}
/**
- * Returns the MSISDN string.
- * for a GSM phone. Return null if it is unavailable.
+ * Returns the MSISDN string for a GSM phone. Return null if it is unavailable.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_SMS READ_SMS},
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+ * that the caller is the default SMS app,
+ * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+ * for any API level.
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * for apps targeting SDK API level 29 and below.
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.READ_PHONE_NUMBERS
+ })
@UnsupportedAppUsage
public String getMsisdn() {
return getMsisdn(getSubId());
}
/**
- * Returns the MSISDN string.
- * for a GSM phone. Return null if it is unavailable.
+ * Returns the MSISDN string for a GSM phone. Return null if it is unavailable.
*
* @param subId for which msisdn is returned
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_SMS READ_SMS},
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+ * that the caller is the default SMS app,
+ * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+ * for any API level.
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * for apps targeting SDK API level 29 and below.
+ *
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.READ_PHONE_NUMBERS
+ })
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public String getMsisdn(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
- return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId());
+ return info.getMsisdnForSubscriber(subId, getOpPackageName(), getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4609,7 +4631,7 @@
if (info == null)
return null;
return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4734,7 +4756,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getVisualVoicemailPackageName(mContext.getOpPackageName(),
- getFeatureId(), getSubId());
+ getAttributionTag(), getSubId());
}
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
@@ -5171,7 +5193,7 @@
if (telephony == null)
return 0;
return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
return 0;
} catch (NullPointerException ex) {
@@ -5208,7 +5230,7 @@
if (info == null)
return null;
return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -5298,8 +5320,8 @@
* not present or not loaded
* @hide
*/
+ @UnsupportedAppUsage
@Nullable
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String[] getIsimImpu() {
try {
@@ -5591,7 +5613,7 @@
(TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
if (telephonyRegistry != null) {
- telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getFeatureId(),
+ telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(),
listener, events, notifyNow);
} else {
Rlog.w(TAG, "telephony registry not ready.");
@@ -5624,7 +5646,7 @@
if (telephony == null)
return -1;
return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
// the phone process is restarting.
return -1;
@@ -5648,7 +5670,7 @@
if (telephony == null)
return -1;
return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
// the phone process is restarting.
return -1;
@@ -5680,7 +5702,7 @@
if (telephony == null)
return null;
return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
// the phone process is restarting.
return null;
@@ -5772,7 +5794,7 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
- return telephony.getAllCellInfo(getOpPackageName(), getFeatureId());
+ return telephony.getAllCellInfo(getOpPackageName(), getAttributionTag());
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
@@ -5872,7 +5894,7 @@
Binder.restoreCallingIdentity(identity);
}
}
- }, getOpPackageName(), getFeatureId());
+ }, getOpPackageName(), getAttributionTag());
} catch (RemoteException ex) {
}
}
@@ -5923,7 +5945,7 @@
Binder.restoreCallingIdentity(identity);
}
}
- }, getOpPackageName(), getFeatureId(), workSource);
+ }, getOpPackageName(), getAttributionTag(), workSource);
} catch (RemoteException ex) {
}
}
@@ -7209,7 +7231,7 @@
if (telephony == null)
return null;
return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -7243,7 +7265,7 @@
ITelephony telephony = getITelephony();
if (telephony == null) return -1;
return telephony.setForbiddenPlmns(
- getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getFeatureId());
+ getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getAttributionTag());
} catch (RemoteException ex) {
Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage());
} catch (NullPointerException ex) {
@@ -7264,7 +7286,7 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return new String[0];
- return telephony.getPcscfAddress(apnType, getOpPackageName(), getFeatureId());
+ return telephony.getPcscfAddress(apnType, getOpPackageName(), getAttributionTag());
} catch (RemoteException e) {
return new String[0];
}
@@ -7837,7 +7859,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getAvailableNetworks RemoteException", ex);
@@ -7892,7 +7914,7 @@
}
}
return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
- getOpPackageName(), getFeatureId());
+ getOpPackageName(), getAttributionTag());
}
/**
@@ -8646,7 +8668,7 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.isRadioOnWithFeature(getOpPackageName(), getFeatureId());
+ return telephony.isRadioOnWithFeature(getOpPackageName(), getAttributionTag());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isRadioOn", e);
}
@@ -8940,7 +8962,10 @@
}
/**
- * Shut down all the live radios over all the slot index.
+ * Shut down all the live radios over all the slot indexes.
+ *
+ * <p>To know when the radio has completed powering off, use
+ * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}.
*
* @hide
*/
@@ -8953,7 +8978,8 @@
telephony.shutdownMobileRadios();
}
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#shutdownMobileRadios", e);
+ Log.e(TAG, "Error calling ITelephony#shutdownAllRadios", e);
+ e.rethrowAsRuntimeException();
}
}
@@ -8972,7 +8998,8 @@
return telephony.needMobileRadioShutdown();
}
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#needMobileRadioShutdown", e);
+ Log.e(TAG, "Error calling ITelephony#isAnyRadioPoweredOn", e);
+ e.rethrowAsRuntimeException();
}
return false;
}
@@ -9015,7 +9042,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
@@ -9202,7 +9229,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @CdmaRoamingMode int getCdmaRoamingMode() {
int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
@@ -9231,7 +9257,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) {
try {
@@ -9257,19 +9282,16 @@
/** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
* @hide
*/
- @SystemApi
public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1;
/** Used for CDMA subscription mode: RUIM/SIM (default)
* @hide
*/
- @SystemApi
public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
/** Used for CDMA subscription mode: NV -> non-volatile memory
* @hide
*/
- @SystemApi
public static final int CDMA_SUBSCRIPTION_NV = 1;
/** @hide */
@@ -9288,7 +9310,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) {
try {
@@ -9406,7 +9427,7 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null)
- return telephony.isVideoCallingEnabled(getOpPackageName(), getFeatureId());
+ return telephony.isVideoCallingEnabled(getOpPackageName(), getAttributionTag());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isVideoCallingEnabled", e);
}
@@ -9423,7 +9444,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e);
@@ -9442,7 +9463,7 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.isWorldPhone(mSubId, getOpPackageName(), getFeatureId());
+ return telephony.isWorldPhone(mSubId, getOpPackageName(), getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isWorldPhone", e);
@@ -9996,18 +10017,30 @@
}
/**
- * Gets the default Respond Via Message application
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * Gets the default Respond Via Message application, updating the cache if there is no
+ * respond-via-message application currently configured.
* @return component name of the app and class to direct Respond Via Message intent to, or
* {@code null} if the functionality is not supported.
* @hide
*/
@SystemApi
@TestApi
- public static @Nullable ComponentName getDefaultRespondViaMessageApplication(
- @NonNull Context context, boolean updateIfNeeded) {
- return SmsApplication.getDefaultRespondViaMessageApplication(context, updateIfNeeded);
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ public @Nullable ComponentName getAndUpdateDefaultRespondViaMessageApplication() {
+ return SmsApplication.getDefaultRespondViaMessageApplication(mContext, true);
+ }
+
+ /**
+ * Gets the default Respond Via Message application.
+ * @return component name of the app and class to direct Respond Via Message intent to, or
+ * {@code null} if the functionality is not supported.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ public @Nullable ComponentName getDefaultRespondViaMessageApplication() {
+ return SmsApplication.getDefaultRespondViaMessageApplication(mContext, false);
}
/**
@@ -10170,7 +10203,8 @@
ITelephony service = getITelephony();
if (service != null) {
retval = service.getSubIdForPhoneAccountHandle(
- phoneAccountHandle, mContext.getOpPackageName(), mContext.getFeatureId());
+ phoneAccountHandle, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
Log.e(TAG, "getSubscriptionId RemoteException", ex);
@@ -10311,7 +10345,7 @@
ITelephony service = getITelephony();
if (service != null) {
return service.getServiceStateForSubscriber(subId, getOpPackageName(),
- getFeatureId());
+ getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
@@ -11032,7 +11066,8 @@
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.getClientRequestStats(getOpPackageName(), getFeatureId(), subId);
+ return service.getClientRequestStats(getOpPackageName(), getAttributionTag(),
+ subId);
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getClientRequestStats", e);
@@ -11143,21 +11178,21 @@
}
/**
- * Checks whether cellular data connection is enabled in the device.
+ * Checks whether cellular data connection is allowed in the device.
*
- * Whether cellular data connection is enabled, meaning upon request whether will try to setup
- * metered data connection considering all factors below:
- * 1) User turned on data setting {@link #isDataEnabled}.
- * 2) Carrier allows data to be on.
- * 3) Network policy.
- * And possibly others.
- *
- * @return {@code true} if the overall data connection is capable; {@code false} if not.
+ * <p>Whether cellular data connection is allowed considers all factors below:
+ * <UL>
+ * <LI>User turned on data setting {@link #isDataEnabled}.</LI>
+ * <LI>Carrier allows data to be on.</LI>
+ * <LI>Network policy.</LI>
+ * <LI>And possibly others.</LI>
+ * </UL>
+ * @return {@code true} if the overall data connection is allowed; {@code false} if not.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public boolean isDataConnectionEnabled() {
+ public boolean isDataConnectionAllowed() {
boolean retVal = false;
try {
int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -11165,8 +11200,7 @@
if (telephony != null)
retVal = telephony.isDataEnabled(subId);
} catch (RemoteException e) {
- Log.e(TAG, "Error isDataConnectionEnabled", e);
- } catch (NullPointerException e) {
+ Log.e(TAG, "Error isDataConnectionAllowed", e);
}
return retVal;
}
@@ -11181,6 +11215,8 @@
* PackageManager.FEATURE_TELEPHONY system feature, which is available
* on any device with a telephony radio, even if the device is
* voice-only.
+ *
+ * @hide
*/
public boolean isDataCapable() {
if (mContext == null) return true;
@@ -11317,7 +11353,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getNumberOfModemsWithSimultaneousDataConnections(
- getSubId(), getOpPackageName(), getFeatureId());
+ getSubId(), getOpPackageName(), getAttributionTag());
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
@@ -11747,7 +11783,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getEmergencyNumberList(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -11802,7 +11838,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
emergencyNumberList = telephony.getEmergencyNumberList(
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
if (emergencyNumberList != null) {
for (Integer subscriptionId : emergencyNumberList.keySet()) {
List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId);
@@ -11895,7 +11931,7 @@
}
/**
- * A test API to return the emergency number db version.
+ * Returns the emergency number database version.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
@@ -11904,6 +11940,7 @@
*/
@TestApi
@SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int getEmergencyNumberDbVersion() {
try {
ITelephony telephony = getITelephony();
@@ -12110,13 +12147,13 @@
})
public int getPreferredOpportunisticDataSubscription() {
String packageName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
- String featureId = mContext != null ? mContext.getFeatureId() : null;
+ String attributionTag = mContext != null ? mContext.getAttributionTag() : null;
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
try {
IOns iOpportunisticNetworkService = getIOns();
if (iOpportunisticNetworkService != null) {
subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(
- packageName, featureId);
+ packageName, attributionTag);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex);
@@ -12233,18 +12270,20 @@
/**
* It indicates whether modem is enabled or not per slot.
- * It's the corresponding status of {@link #enableModemForSlot}.
+ * It's the corresponding status of TelephonyManager.enableModemForSlot.
*
+ * <p>Requires Permission:
+ * READ_PRIVILEGED_PHONE_STATE or
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* @param slotIndex which slot it's checking.
- * @hide
*/
- @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isModemEnabledForSlot(int slotIndex) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.isModemEnabledForSlot(slotIndex, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
}
} catch (RemoteException ex) {
Log.e(TAG, "enableModem RemoteException", ex);
@@ -12349,7 +12388,7 @@
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.isMultiSimSupported(getOpPackageName(), getFeatureId());
+ return service.isMultiSimSupported(getOpPackageName(), getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "isMultiSimSupported RemoteException", e);
@@ -12401,7 +12440,7 @@
ITelephony service = getITelephony();
if (service != null) {
return service.doesSwitchMultiSimConfigTriggerReboot(getSubId(),
- getOpPackageName(), getFeatureId());
+ getOpPackageName(), getAttributionTag());
}
} catch (RemoteException e) {
Log.e(TAG, "doesSwitchMultiSimConfigTriggerReboot RemoteException", e);
@@ -12448,7 +12487,7 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public int getCarrierPrivilegeStatus(int uid) {
+ public @CarrierPrivilegeStatus int getCarrierPrivilegeStatus(int uid) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -12470,7 +12509,6 @@
* @throws {@link SecurityException} if the caller is not the system or phone process.
* @hide
*/
- @SystemApi
@TestApi
// TODO: add new permission tag indicating that this is system-only.
public @NonNull List<ApnSetting> getDevicePolicyOverrideApns(@NonNull Context context) {
@@ -12501,7 +12539,6 @@
* @throws {@link SecurityException} if the caller is not the system or phone process.
* @hide
*/
- @SystemApi
@TestApi
// TODO: add new permission tag indicating that this is system-only.
public int addDevicePolicyOverrideApn(@NonNull Context context,
@@ -12532,7 +12569,6 @@
* @throws {@link SecurityException} if the caller is not the system or phone process.
* @hide
*/
- @SystemApi
@TestApi
// TODO: add new permission tag indicating that this is system-only.
public boolean modifyDevicePolicyOverrideApn(@NonNull Context context, int apnId,
@@ -12716,7 +12752,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@NonNull
public CallForwardingInfo getCallForwarding(@CallForwardingReason int callForwardingReason) {
@@ -12763,7 +12798,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo) {
if (callForwardingInfo == null) {
@@ -12804,7 +12838,6 @@
*
* @hide
*/
- @SystemApi
public static final int CALL_WAITING_STATUS_ACTIVE = 1;
/**
@@ -12812,7 +12845,6 @@
*
* @hide
*/
- @SystemApi
public static final int CALL_WAITING_STATUS_INACTIVE = 2;
/**
@@ -12820,7 +12852,6 @@
*
* @hide
*/
- @SystemApi
public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3;
/**
@@ -12828,10 +12859,24 @@
*
* @hide
*/
- @SystemApi
public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
/**
+ * Call waiting function status
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
+ CALL_WAITING_STATUS_ACTIVE,
+ CALL_WAITING_STATUS_INACTIVE,
+ CALL_WAITING_STATUS_NOT_SUPPORTED,
+ CALL_WAITING_STATUS_UNKNOWN_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallWaitingStatus {
+ }
+
+ /**
* Gets the status of voice call waiting function. Call waiting function enables the waiting
* for the incoming call when it reaches the user who is busy to make another call and allows
* users to decide whether to switch to the incoming call.
@@ -12839,7 +12884,6 @@
* @return the status of call waiting function.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @CallWaitingStatus int getCallWaitingStatus() {
try {
@@ -12865,7 +12909,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setCallWaitingStatus(boolean isEnable) {
try {
@@ -12896,7 +12939,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setDataAllowedDuringVoiceCall(boolean allow) {
try {
@@ -12925,7 +12967,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isDataAllowedInVoiceCall() {
try {
@@ -12972,7 +13013,6 @@
* The IccLock state or password was changed successfully.
* @hide
*/
- @SystemApi
public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE;
/**
@@ -12985,7 +13025,6 @@
*
* @hide
*/
- @SystemApi
@WorkerThread
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isIccLockEnabled() {
@@ -13022,7 +13061,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public int setIccLockEnabled(boolean enabled, @NonNull String password) {
checkNotNull(password, "setIccLockEnabled password can't be null.");
@@ -13056,7 +13094,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public int changeIccLockPassword(@NonNull String oldPassword, @NonNull String newPassword) {
checkNotNull(oldPassword, "changeIccLockPassword oldPassword can't be null.");
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 6c4e7ce..f56bbe1 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -458,6 +458,9 @@
* this method to facilitate the creation of {@link DataServiceProvider} instances. The system
* will call this method after binding the data service for each active SIM slot id.
*
+ * This methead is guaranteed to be invoked in {@link DataService}'s internal handler thread
+ * whose looper can be retrieved with {@link Looper.myLooper()} when override this method.
+ *
* @param slotIndex SIM slot id the data service associated with.
* @return Data service object. Null if failed to create the provider (e.g. invalid slot index)
*/
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 3341fa7..bd531da 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -21,7 +21,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
@@ -124,7 +123,7 @@
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
@Override
- public void onUnregistered(@Nullable ImsReasonInfo info) {
+ public void onUnregistered(@NonNull ImsReasonInfo info) {
}
/**
@@ -136,7 +135,7 @@
@Override
public void onTechnologyChangeFailed(
@AccessNetworkConstants.TransportType int imsTransportType,
- @Nullable ImsReasonInfo info) {
+ @NonNull ImsReasonInfo info) {
}
}
@@ -294,8 +293,15 @@
throw new IllegalArgumentException("Must include a non-null Executor.");
}
c.setExecutor(executor);
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
try {
- getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+ iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -331,8 +337,15 @@
throw new IllegalArgumentException("Must include a non-null Executor.");
}
c.setExecutor(executor);
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
try {
- getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+ iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
} catch (ServiceSpecificException e) {
throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException | IllegalStateException e) {
@@ -361,8 +374,14 @@
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
+ iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -387,8 +406,14 @@
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
+ iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -409,8 +434,14 @@
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+ iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
executor.execute(() -> stateCallback.accept(result));
@@ -443,8 +474,14 @@
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().getImsMmTelRegistrationTransportType(mSubId,
+ iTelephony.getImsMmTelRegistrationTransportType(mSubId,
new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
@@ -506,8 +543,15 @@
throw new IllegalArgumentException("Must include a non-null Executor.");
}
c.setExecutor(executor);
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+ }
+
try {
- getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
+ iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder());
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -553,8 +597,14 @@
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
+ iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -599,8 +649,13 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public boolean isAdvancedCallingSettingEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+ return iTelephony.isAdvancedCallingSettingEnabled(mSubId);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -640,8 +695,13 @@
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi @TestApi
public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled);
+ iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -680,8 +740,13 @@
@SystemApi @TestApi
public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().isCapable(mSubId, capability, imsRegTech);
+ return iTelephony.isCapable(mSubId, capability, imsRegTech);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -709,8 +774,13 @@
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().isAvailable(mSubId, capability, imsRegTech);
+ return iTelephony.isAvailable(mSubId, capability, imsRegTech);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -744,6 +814,13 @@
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
try {
getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
@Override
@@ -788,8 +865,13 @@
android.Manifest.permission.READ_PRECISE_PHONE_STATE})
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
public boolean isVtSettingEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().isVtSettingEnabled(mSubId);
+ return iTelephony.isVtSettingEnabled(mSubId);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -813,8 +895,13 @@
@SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVtSettingEnabled(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().setVtSettingEnabled(mSubId, isEnabled);
+ iTelephony.setVtSettingEnabled(mSubId, isEnabled);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -853,8 +940,13 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public boolean isVoWiFiSettingEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().isVoWiFiSettingEnabled(mSubId);
+ return iTelephony.isVoWiFiSettingEnabled(mSubId);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -879,8 +971,13 @@
@SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiSettingEnabled(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled);
+ iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -921,8 +1018,13 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public boolean isVoWiFiRoamingSettingEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+ return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -948,8 +1050,13 @@
@SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
+ iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -980,8 +1087,13 @@
@SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
+ iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -1025,8 +1137,13 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public @WiFiCallingMode int getVoWiFiModeSetting() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().getVoWiFiModeSetting(mSubId);
+ return iTelephony.getVoWiFiModeSetting(mSubId);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -1054,8 +1171,13 @@
@SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().setVoWiFiModeSetting(mSubId, mode);
+ iTelephony.setVoWiFiModeSetting(mSubId, mode);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -1085,8 +1207,13 @@
@SystemApi @TestApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+ return iTelephony.getVoWiFiRoamingModeSetting(mSubId);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -1116,8 +1243,13 @@
@SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
+ iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -1145,8 +1277,13 @@
@SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRttCapabilitySetting(boolean isEnabled) {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
+ iTelephony.setRttCapabilitySetting(mSubId, isEnabled);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -1186,8 +1323,13 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public boolean isTtyOverVolteEnabled() {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+
try {
- return getITelephony().isTtyOverVolteEnabled(mSubId);
+ return iTelephony.isTtyOverVolteEnabled(mSubId);
} catch (ServiceSpecificException e) {
if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
// Rethrow as runtime error to keep API compatible.
@@ -1223,8 +1365,15 @@
if (callback == null) {
throw new IllegalArgumentException("Must include a non-null Consumer.");
}
+
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
try {
- getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
+ iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
executor.execute(() -> callback.accept(result));
@@ -1243,9 +1392,6 @@
.getTelephonyServiceManager()
.getTelephonyServiceRegisterer()
.get());
- if (binder == null) {
- throw new RuntimeException("Could not find Telephony Service.");
- }
return binder;
}
}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 00fa942..1a606b7 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -85,6 +85,7 @@
/**
* There is no existing configuration for the queried provisioning key.
+ * @hide
*/
public static final int PROVISIONING_RESULT_UNKNOWN = -1;
@@ -120,6 +121,7 @@
* Value is in String format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0;
@@ -143,6 +145,7 @@
* Value is in String format.
* @see #setProvisioningStringValue(int, String)
* @see #getProvisioningStringValue(int)
+ * @hide
*/
public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1;
@@ -154,6 +157,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_SESSION_TIMER_SEC = 2;
@@ -165,6 +169,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3;
@@ -176,6 +181,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4;
@@ -184,6 +190,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5;
@@ -192,6 +199,7 @@
* Value is in boolean format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_ENABLE_SILENT_REDIAL = 6;
@@ -207,6 +215,7 @@
* The value is an integer.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_T1_TIMER_VALUE_MS = 7;
@@ -218,6 +227,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_T2_TIMER_VALUE_MS = 8;
@@ -229,6 +239,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_TF_TIMER_VALUE_MS = 9;
@@ -241,6 +252,7 @@
* {@link #PROVISIONING_VALUE_DISABLED} to disable VoLTE provisioning.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VOLTE_PROVISIONING_STATUS = 10;
@@ -253,6 +265,7 @@
* {@link #PROVISIONING_VALUE_DISABLED} to disable VT provisioning.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VT_PROVISIONING_STATUS = 11;
@@ -261,6 +274,7 @@
* Value is in String format.
* @see #setProvisioningStringValue(int, String)
* @see #getProvisioningStringValue(int)
+ * @hide
*/
public static final int KEY_REGISTRATION_DOMAIN_NAME = 12;
@@ -270,18 +284,21 @@
* Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SMS_FORMAT = 13;
/**
* Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used.
* See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information.
+ * @hide
*/
public static final int SMS_FORMAT_3GPP2 = 0;
/**
* Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used.
* See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information.
+ * @hide
*/
public static final int SMS_FORMAT_3GPP = 1;
@@ -290,6 +307,7 @@
* Value is in Integer format. ON (1), OFF(0).
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SMS_OVER_IP_ENABLED = 14;
@@ -300,6 +318,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15;
@@ -310,6 +329,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16;
@@ -322,6 +342,7 @@
* enabled.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17;
@@ -334,6 +355,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18;
@@ -345,6 +367,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19;
@@ -356,6 +379,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20;
@@ -366,6 +390,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21;
@@ -376,6 +401,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
@@ -387,6 +413,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
@@ -395,6 +422,7 @@
* Value is in Integer format. Enable (1), Disable(0).
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24;
@@ -407,6 +435,7 @@
* {@link #PROVISIONING_VALUE_DISABLED} to disable EAB provisioning.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_EAB_PROVISIONING_STATUS = 25;
@@ -440,6 +469,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28;
@@ -448,6 +478,7 @@
* Value is in Integer format. On (1), OFF(0).
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_MOBILE_DATA_ENABLED = 29;
@@ -456,12 +487,14 @@
* Value is in Integer format. Opted-in (1) Opted-out (0).
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30;
/**
* Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
* Value is in String format.
+ * @hide
*/
public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31;
@@ -470,6 +503,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
@@ -479,6 +513,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
@@ -488,6 +523,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34;
@@ -496,6 +532,7 @@
* Value is in integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RTP_SPEECH_START_PORT = 35;
@@ -505,6 +542,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RTP_SPEECH_END_PORT = 36;
@@ -514,6 +552,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37;
@@ -523,6 +562,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38;
@@ -532,6 +572,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39;
@@ -541,6 +582,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40;
@@ -550,6 +592,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41;
@@ -559,6 +602,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42;
@@ -568,6 +612,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43;
@@ -577,6 +622,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44;
@@ -586,6 +632,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45;
@@ -595,6 +642,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46;
@@ -603,6 +651,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47;
@@ -611,6 +660,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48;
@@ -619,6 +669,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49;
@@ -627,6 +678,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50;
@@ -635,6 +687,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51;
@@ -643,6 +696,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52;
@@ -651,12 +705,14 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53;
/**
* SMS Public Service Identity.
* Value is in String format.
+ * @hide
*/
public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54;
@@ -666,16 +722,19 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VIDEO_QUALITY = 55;
/**
* Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality.
+ * @hide
*/
public static final int VIDEO_QUALITY_LOW = 0;
/**
* Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality.
+ * @hide
*/
public static final int VIDEO_QUALITY_HIGH = 1;
@@ -685,6 +744,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_LTE_THRESHOLD_1 = 56;
@@ -696,6 +756,7 @@
*
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_LTE_THRESHOLD_2 = 57;
@@ -707,6 +768,7 @@
*
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_LTE_THRESHOLD_3 = 58;
@@ -716,6 +778,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_1X_THRESHOLD = 59;
@@ -727,6 +790,7 @@
*
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_WIFI_THRESHOLD_A = 60;
@@ -737,6 +801,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_WIFI_THRESHOLD_B = 61;
@@ -746,6 +811,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_LTE_EPDG_TIMER_SEC = 62;
@@ -755,12 +821,14 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_WIFI_EPDG_TIMER_SEC = 63;
/**
* 1x ePDG timer (in seconds).
* Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+ * @hide
*/
public static final int KEY_1X_EPDG_TIMER_SEC = 64;
@@ -769,6 +837,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_MULTIENDPOINT_ENABLED = 65;
@@ -777,6 +846,7 @@
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RTT_ENABLED = 66;
@@ -928,7 +998,7 @@
*
* @param key An integer that represents the provisioning key, which is defined by the OEM.
* @return an integer value for the provided key, or
- * {@link #PROVISIONING_RESULT_UNKNOWN} if the key doesn't exist.
+ * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
* @throws IllegalArgumentException if the key provided was invalid.
*/
@WorkerThread
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 57b9b7a..dc36edf 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -19,8 +19,6 @@
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,8 +35,6 @@
* Contains the User Capability Exchange capabilities corresponding to a contact's URI.
* @hide
*/
-@SystemApi
-@TestApi
public final class RcsContactUceCapability implements Parcelable {
/** Supports 1-to-1 chat */
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 30306c7..05ab6bd 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -291,7 +291,7 @@
try {
imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
- mContext.getFeatureId(), contactNumbers, internalCallback);
+ mContext.getAttributionTag(), contactNumbers, internalCallback);
} catch (RemoteException e) {
Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
throw new ImsException("Remote IMS Service is not available",
@@ -352,7 +352,7 @@
try {
// Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this.
return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e);
throw new ImsException("Remote IMS Service is not available",
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 5c86ba7..1dbaff5 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -196,11 +196,11 @@
}
/**
- * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+ * Notifies the framework when the IMS Provider is unregistered from the IMS network.
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
- public void onUnregistered(@Nullable ImsReasonInfo info) {
+ public void onUnregistered(@NonNull ImsReasonInfo info) {
}
/**
@@ -211,7 +211,7 @@
*/
public void onTechnologyChangeFailed(
@AccessNetworkConstants.TransportType int imsTransportType,
- @Nullable ImsReasonInfo info) {
+ @NonNull ImsReasonInfo info) {
}
/**
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 14a64d2..7069e0a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -175,9 +175,11 @@
*/
public final void onDeregistered(ImsReasonInfo info) {
updateToDisconnectedState(info);
+ // ImsReasonInfo should never be null.
+ final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
mCallbacks.broadcastAction((c) -> {
try {
- c.onDeregistered(info);
+ c.onDeregistered(reasonInfo);
} catch (RemoteException e) {
Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
"callback.");
@@ -194,9 +196,10 @@
*/
public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
+ final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
mCallbacks.broadcastAction((c) -> {
try {
- c.onTechnologyChangeFailed(imsRadioTech, info);
+ c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
} catch (RemoteException e) {
Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
"callback.");
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index f13371c..8564f7a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -44,52 +44,62 @@
public class ImsUtImplBase {
/**
* Bar all incoming calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_ALL_INCOMING = 1;
/**
* Bar all outgoing calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_ALL_OUTGOING = 2;
/**
* Bar all outgoing international calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_OUTGOING_INTL = 3;
/**
* Bar all outgoing international calls, excluding those to the home PLMN country
* (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4;
/**
* Bar all incoming calls when roaming (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5;
/**
* Enable Anonymous Communication Rejection (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6;
/**
* Bar all incoming and outgoing calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_ALL = 7;
/**
* Bar all outgoing service requests, including calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8;
/**
* Bar all incoming service requests, including calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9;
/**
* Bar specific incoming calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10;
@@ -104,6 +114,7 @@
/**
* Constant used to denote an invalid return value.
+ * @hide
*/
public static final int INVALID_RESULT = -1;
@@ -338,6 +349,7 @@
/**
* Updates the configuration of the call barring for specified service class with password.
+ * @hide
*/
public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
int serviceClass, @NonNull String password) {
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index ae20cae..629b6c7 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -98,7 +98,8 @@
mDevice = getDevice();
String apkVerityMode = mDevice.getProperty("ro.apk_verity.mode");
- assumeTrue(APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
+ assumeTrue(mDevice.getLaunchApiLevel() >= 30
+ || APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
mKeyId = expectRemoteCommandToSucceed(
"mini-keyctl padd asymmetric fsv_test .fs-verity < " + CERT_PATH).trim();
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
index 504bd17..b805744 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
@@ -38,38 +38,75 @@
public class DummyBlobData {
private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L;
- private final Context mContext;
private final Random mRandom;
private final File mFile;
private final long mFileSize;
- private final String mLabel;
+ private final CharSequence mLabel;
byte[] mFileDigest;
long mExpiryTimeMs;
- public DummyBlobData(Context context) {
- this(context, new Random(0), "blob_" + System.nanoTime());
+ public DummyBlobData(Builder builder) {
+ mRandom = new Random(builder.getRandomSeed());
+ mFile = new File(builder.getContext().getFilesDir(), builder.getFileName());
+ mFileSize = builder.getFileSize();
+ mLabel = builder.getLabel();
}
- public DummyBlobData(Context context, long fileSize) {
- this(context, fileSize, new Random(0), "blob_" + System.nanoTime(), "Test label");
- }
+ public static class Builder {
+ private final Context mContext;
+ private int mRandomSeed = 0;
+ private long mFileSize = DEFAULT_SIZE_BYTES;
+ private CharSequence mLabel = "Test label";
+ private String mFileName = "blob_" + System.nanoTime();
- public DummyBlobData(Context context, Random random, String fileName) {
- this(context, DEFAULT_SIZE_BYTES, random, fileName, "Test label");
- }
+ public Builder(Context context) {
+ mContext = context;
+ }
- public DummyBlobData(Context context, Random random, String fileName, String label) {
- this(context, DEFAULT_SIZE_BYTES, random, fileName, label);
- }
+ public Context getContext() {
+ return mContext;
+ }
- public DummyBlobData(Context context, long fileSize, Random random, String fileName,
- String label) {
- mContext = context;
- mRandom = random;
- mFile = new File(mContext.getFilesDir(), fileName);
- mFileSize = fileSize;
- mLabel = label;
+ public Builder setRandomSeed(int randomSeed) {
+ mRandomSeed = randomSeed;
+ return this;
+ }
+
+ public int getRandomSeed() {
+ return mRandomSeed;
+ }
+
+ public Builder setFileSize(int fileSize) {
+ mFileSize = fileSize;
+ return this;
+ }
+
+ public long getFileSize() {
+ return mFileSize;
+ }
+
+ public Builder setLabel(CharSequence label) {
+ mLabel = label;
+ return this;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public Builder setFileName(String fileName) {
+ mFileName = fileName;
+ return this;
+ }
+
+ public String getFileName() {
+ return mFileName;
+ }
+
+ public DummyBlobData build() {
+ return new DummyBlobData(this);
+ }
}
public void prepare() throws Exception {
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
index c35385c..654c1e2 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
@@ -16,7 +16,13 @@
package com.android.utils.blob;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.blob.BlobHandle;
import android.app.blob.BlobStoreManager;
+import android.app.blob.LeaseInfo;
+import android.content.Context;
+import android.content.res.Resources;
import android.os.ParcelFileDescriptor;
import java.io.FileInputStream;
@@ -56,4 +62,76 @@
copy(in, out, lengthBytes);
}
}
+
+ public static void assertLeasedBlobs(BlobStoreManager blobStoreManager,
+ BlobHandle... expectedBlobHandles) throws IOException {
+ assertThat(blobStoreManager.getLeasedBlobs()).containsExactly(expectedBlobHandles);
+ }
+
+ public static void assertNoLeasedBlobs(BlobStoreManager blobStoreManager)
+ throws IOException {
+ assertThat(blobStoreManager.getLeasedBlobs()).isEmpty();
+ }
+
+ public static void acquireLease(Context context,
+ BlobHandle blobHandle, CharSequence description) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.acquireLease(blobHandle, description);
+
+ final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
+ assertLeaseInfo(leaseInfo, context.getPackageName(), 0,
+ Resources.ID_NULL, description);
+ }
+
+ public static void acquireLease(Context context,
+ BlobHandle blobHandle, int descriptionResId) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.acquireLease(blobHandle, descriptionResId);
+
+ final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
+ assertLeaseInfo(leaseInfo, context.getPackageName(), 0,
+ descriptionResId, context.getString(descriptionResId));
+ }
+
+ public static void acquireLease(Context context,
+ BlobHandle blobHandle, CharSequence description,
+ long expiryTimeMs) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.acquireLease(blobHandle, description, expiryTimeMs);
+
+ final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
+ assertLeaseInfo(leaseInfo, context.getPackageName(), expiryTimeMs,
+ Resources.ID_NULL, description);
+ }
+
+ public static void acquireLease(Context context,
+ BlobHandle blobHandle, int descriptionResId,
+ long expiryTimeMs) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.acquireLease(blobHandle, descriptionResId, expiryTimeMs);
+
+ final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
+ assertLeaseInfo(leaseInfo, context.getPackageName(), expiryTimeMs,
+ descriptionResId, context.getString(descriptionResId));
+ }
+
+ public static void releaseLease(Context context,
+ BlobHandle blobHandle) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.releaseLease(blobHandle);
+ assertThat(blobStoreManager.getLeaseInfo(blobHandle)).isNull();
+ }
+
+ private static void assertLeaseInfo(LeaseInfo leaseInfo, String packageName,
+ long expiryTimeMs, int descriptionResId, CharSequence description) {
+ assertThat(leaseInfo.getPackageName()).isEqualTo(packageName);
+ assertThat(leaseInfo.getExpiryTimeMillis()).isEqualTo(expiryTimeMs);
+ assertThat(leaseInfo.getDescriptionResId()).isEqualTo(descriptionResId);
+ assertThat(leaseInfo.getDescription()).isEqualTo(description);
+ }
}
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 5904916..c7e5a5e 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -322,7 +322,7 @@
mAtm.startActivityAndWait(null,
getInstrumentation().getContext().getBasePackageName(),
- getInstrumentation().getContext().getFeatureId(), mLaunchIntent,
+ getInstrumentation().getContext().getAttributionTag(), mLaunchIntent,
mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null,
UserHandle.USER_CURRENT_OR_SELF);
} catch (RemoteException e) {
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 819fc02..2957192 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -1064,6 +1064,20 @@
}
/**
+ * Ensure that passing a null list of failed packages does not cause any mitigation logic to
+ * execute.
+ */
+ @Test
+ public void testNullFailedPackagesList() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+ watchdog.startObservingHealth(observer1, List.of(APP_A), LONG_DURATION);
+
+ raiseFatalFailureAndDispatch(watchdog, null, PackageWatchdog.FAILURE_REASON_APP_CRASH);
+ assertThat(observer1.mMitigatedPackages).isEmpty();
+ }
+
+ /**
* Test to verify that Package Watchdog syncs health check requests with the controller
* correctly, and that the requests are only synced when the set of observed packages
* changes.
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
index 6169671..3bc5309 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
@@ -66,7 +66,7 @@
WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(500, 500, WindowManager.LayoutParams.TYPE_APPLICATION,
0, PixelFormat.OPAQUE);
- mVr.addView(v, lp);
+ mVr.setView(v, lp);
}
@Override
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index e415170..548af0c 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -34,9 +34,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.Window;
import android.view.WindowInsets;
-import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
@@ -101,7 +99,7 @@
&& !mRequestedController) {
mRequestedController = true;
v.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
- 1000, new LinearInterpolator(),
+ 1000, new LinearInterpolator(), null /* cancellationSignal */,
mCurrentRequest = new WindowInsetsAnimationControlListener() {
@Override
public void onReady(
@@ -116,7 +114,14 @@
}
@Override
- public void onCancelled() {
+ public void onFinished(
+ WindowInsetsAnimationController controller) {
+ mAnimationController = null;
+ }
+
+ @Override
+ public void onCancelled(
+ WindowInsetsAnimationController controller) {
mAnimationController = null;
}
});
@@ -208,7 +213,7 @@
if ((types & ime()) != 0 && !hasControl) {
hasControl = true;
controller.controlWindowInsetsAnimation(ime(), -1,
- new LinearInterpolator(),
+ new LinearInterpolator(), null /* cancellationSignal */,
new WindowInsetsAnimationControlListener() {
@Override
public void onReady(
@@ -232,7 +237,13 @@
}
@Override
- public void onCancelled() {
+ public void onFinished(
+ WindowInsetsAnimationController controller) {
+ }
+
+ @Override
+ public void onCancelled(
+ WindowInsetsAnimationController controller) {
}
});
}
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index 480b12b..009f817 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -47,6 +47,7 @@
<uses-permission android:name="android.permission.NETWORK_STACK" />
<uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.NETWORK_FACTORY" />
+ <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt
new file mode 100644
index 0000000..f464ec6
--- /dev/null
+++ b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt
@@ -0,0 +1,120 @@
+/*
+ * 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 android.net
+
+import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS
+import android.net.InvalidPacketException.ERROR_INVALID_PORT
+import android.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import java.net.InetAddress
+import java.util.Arrays
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class KeepalivePacketDataTest {
+ @Rule @JvmField
+ val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+ private val INVALID_PORT = 65537
+ private val TEST_DST_PORT = 4244
+ private val TEST_SRC_PORT = 4243
+
+ private val TESTBYTES = byteArrayOf(12, 31, 22, 44)
+ private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
+ private val TEST_DST_ADDRV4 = "198.168.0.1".address()
+ private val TEST_ADDRV6 = "2001:db8::1".address()
+
+ private fun String.address() = InetAddresses.parseNumericAddress(this)
+
+ // Add for test because constructor of KeepalivePacketData is protected.
+ private inner class TestKeepalivePacketData(
+ srcAddress: InetAddress? = TEST_SRC_ADDRV4,
+ srcPort: Int = TEST_SRC_PORT,
+ dstAddress: InetAddress? = TEST_DST_ADDRV4,
+ dstPort: Int = TEST_DST_PORT,
+ data: ByteArray = TESTBYTES
+ ) : KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testConstructor() {
+ var data: TestKeepalivePacketData
+
+ try {
+ data = TestKeepalivePacketData(srcAddress = null)
+ fail("Null src address should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstAddress = null)
+ fail("Null dst address should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstAddress = TEST_ADDRV6)
+ fail("Ip family mismatched should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(srcPort = INVALID_PORT)
+ fail("Invalid srcPort should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstPort = INVALID_PORT)
+ fail("Invalid dstPort should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testSrcAddress() = assertEquals(TEST_SRC_ADDRV4, TestKeepalivePacketData().srcAddress)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testDstAddress() = assertEquals(TEST_DST_ADDRV4, TestKeepalivePacketData().dstAddress)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testSrcPort() = assertEquals(TEST_SRC_PORT, TestKeepalivePacketData().srcPort)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testDstPort() = assertEquals(TEST_DST_PORT, TestKeepalivePacketData().dstPort)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testPacket() = assertTrue(Arrays.equals(TESTBYTES, TestKeepalivePacketData().packet))
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 99dac14..c74c112 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -367,6 +367,9 @@
-2, 100000L);
fail("negative deprecation time should cause exception");
} catch (IllegalArgumentException expected) { }
+
+ LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L);
+ assertEquals(100000L, addr.getDeprecationTime());
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -382,6 +385,9 @@
100000L, -2);
fail("negative expiration time should cause exception");
} catch (IllegalArgumentException expected) { }
+
+ LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L);
+ assertEquals(200000L, addr.getExpirationTime());
}
@Test
diff --git a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
new file mode 100644
index 0000000..46f39dd
--- /dev/null
+++ b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
@@ -0,0 +1,114 @@
+/*
+ * 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 android.net
+
+import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS
+import android.net.InvalidPacketException.ERROR_INVALID_PORT
+import android.net.NattSocketKeepalive.NATT_PORT
+import android.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.assertEqualBothWays
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertParcelSane
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.parcelingRoundTrip
+import java.net.InetAddress
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NattKeepalivePacketDataTest {
+ @Rule @JvmField
+ val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+ /* Refer to the definition in {@code NattKeepalivePacketData} */
+ private val IPV4_HEADER_LENGTH = 20
+ private val UDP_HEADER_LENGTH = 8
+
+ private val TEST_PORT = 4243
+ private val TEST_PORT2 = 4244
+ private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
+ private val TEST_DST_ADDRV4 = "198.168.0.1".address()
+ private val TEST_ADDRV6 = "2001:db8::1".address()
+
+ private fun String.address() = InetAddresses.parseNumericAddress(this)
+ private fun nattKeepalivePacket(
+ srcAddress: InetAddress? = TEST_SRC_ADDRV4,
+ srcPort: Int = TEST_PORT,
+ dstAddress: InetAddress? = TEST_DST_ADDRV4,
+ dstPort: Int = NATT_PORT
+ ) = NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort, dstAddress, dstPort)
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testConstructor() {
+ try {
+ nattKeepalivePacket(dstPort = TEST_PORT)
+ fail("Dst port is not NATT port should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+
+ try {
+ nattKeepalivePacket(srcAddress = TEST_ADDRV6)
+ fail("A v6 srcAddress should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ nattKeepalivePacket(dstAddress = TEST_ADDRV6)
+ fail("A v6 dstAddress should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ parcelingRoundTrip(
+ NattKeepalivePacketData(TEST_SRC_ADDRV4, TEST_PORT, TEST_DST_ADDRV4, TEST_PORT,
+ byteArrayOf(12, 31, 22, 44)))
+ fail("Invalid data should cause exception")
+ } catch (e: IllegalArgumentException) { }
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testParcel() {
+ assertParcelSane(nattKeepalivePacket(), 0)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testEquals() {
+ assertEqualBothWays(nattKeepalivePacket(), nattKeepalivePacket())
+ assertNotEquals(nattKeepalivePacket(dstAddress = TEST_SRC_ADDRV4), nattKeepalivePacket())
+ assertNotEquals(nattKeepalivePacket(srcAddress = TEST_DST_ADDRV4), nattKeepalivePacket())
+ // Test src port only because dst port have to be NATT_PORT
+ assertNotEquals(nattKeepalivePacket(srcPort = TEST_PORT2), nattKeepalivePacket())
+ // Make sure the parceling test is updated if fields are added in the base class.
+ assertFieldCountEquals(5, KeepalivePacketData::class.java)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testHashCode() {
+ assertEquals(nattKeepalivePacket().hashCode(), nattKeepalivePacket().hashCode())
+ }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
index 173dbd1..de65ba2 100644
--- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -22,6 +22,9 @@
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.assertParcelSane
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,4 +46,27 @@
}.build()
assertParcelSane(config, 9)
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testBuilder() {
+ val config = NetworkAgentConfig.Builder().apply {
+ setExplicitlySelected(true)
+ setLegacyType(ConnectivityManager.TYPE_ETHERNET)
+ setSubscriberId("MySubId")
+ setPartialConnectivityAcceptable(false)
+ setUnvalidatedConnectivityAcceptable(true)
+ setLegacyTypeName("TEST_NETWORK")
+ disableNat64Detection()
+ disableProvisioningNotification()
+ }.build()
+
+ assertTrue(config.isExplicitlySelected())
+ assertEquals(ConnectivityManager.TYPE_ETHERNET, config.getLegacyType())
+ assertEquals("MySubId", config.getSubscriberId())
+ assertFalse(config.isPartialConnectivityAcceptable())
+ assertTrue(config.isUnvalidatedConnectivityAcceptable())
+ assertEquals("TEST_NETWORK", config.getLegacyTypeName())
+ assertFalse(config.isNat64DetectionEnabled())
+ assertFalse(config.isProvisioningNotificationEnabled())
+ }
}
diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
index 9c7cfb0..aaf97f3 100644
--- a/tests/net/common/java/android/net/util/SocketUtilsTest.kt
+++ b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package android.net.util;
+package android.net.util
+import android.os.Build
import android.system.NetlinkSocketAddress
import android.system.Os
import android.system.OsConstants.AF_INET
@@ -26,18 +27,26 @@
import android.system.PacketSocketAddress
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
private const val TEST_INDEX = 123
private const val TEST_PORT = 555
+private const val FF_BYTE = 0xff.toByte()
+
@RunWith(AndroidJUnit4::class)
@SmallTest
class SocketUtilsTest {
+ @Rule @JvmField
+ val ignoreRule = DevSdkIgnoreRule()
+
@Test
fun testMakeNetlinkSocketAddress() {
val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH)
@@ -50,16 +59,21 @@
}
@Test
- fun testMakePacketSocketAddress() {
+ fun testMakePacketSocketAddress_Q() {
val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX)
assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
- val ff = 0xff.toByte()
- val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX,
- byteArrayOf(ff, ff, ff, ff, ff, ff))
+ val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, ByteArray(6) { FF_BYTE })
assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress)
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testMakePacketSocketAddress() {
+ val pkAddress = SocketUtils.makePacketSocketAddress(
+ ETH_P_ALL, TEST_INDEX, ByteArray(6) { FF_BYTE })
+ assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
+ }
+
@Test
fun testCloseSocket() {
// Expect no exception happening with null object.
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 8eb5cfa..1d6c107 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -304,12 +304,12 @@
}
@Test
- public void testConnectivityDiagnosticsCallbackOnConnectivityReport() {
- mBinder.onConnectivityReport(createSampleConnectivityReport());
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() {
+ mBinder.onConnectivityReportAvailable(createSampleConnectivityReport());
// The callback will be invoked synchronously by inline executor. Immediately check the
// latch without waiting.
- verify(mCb).onConnectivityReport(eq(createSampleConnectivityReport()));
+ verify(mCb).onConnectivityReportAvailable(eq(createSampleConnectivityReport()));
}
@Test
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
index b81ca36..442ac56 100644
--- a/tests/net/java/android/net/IpMemoryStoreTest.java
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -35,6 +35,7 @@
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.NetworkAttributesParcelable;
import android.net.ipmemorystore.Status;
+import android.net.networkstack.ModuleNetworkStackClient;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
@@ -67,7 +68,7 @@
@Mock
Context mMockContext;
@Mock
- NetworkStackClient mNetworkStackClient;
+ ModuleNetworkStackClient mModuleNetworkStackClient;
@Mock
IIpMemoryStore mMockService;
@Mock
@@ -90,14 +91,14 @@
((IIpMemoryStoreCallbacks) invocation.getArgument(0))
.onIpMemoryStoreFetched(mMockService);
return null;
- }).when(mNetworkStackClient).fetchIpMemoryStore(any());
+ }).when(mModuleNetworkStackClient).fetchIpMemoryStore(any());
} else {
- doNothing().when(mNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture());
+ doNothing().when(mModuleNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture());
}
mStore = new IpMemoryStore(mMockContext) {
@Override
- protected NetworkStackClient getNetworkStackClient() {
- return mNetworkStackClient;
+ protected ModuleNetworkStackClient getModuleNetworkStackClient(Context ctx) {
+ return mModuleNetworkStackClient;
}
};
}
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 33d77d2..e71d599 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -64,15 +64,15 @@
@Test
public void testFindIndex() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 5)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12)
- .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12);
assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
@@ -94,21 +94,21 @@
@Test
public void testFindIndexHinted() {
final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10)
- .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
- .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12);
// verify that we correctly find across regardless of hinting
@@ -143,27 +143,27 @@
assertEquals(0, stats.size());
assertEquals(4, stats.internalSize());
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
assertEquals(4, stats.size());
assertEquals(4, stats.internalSize());
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
assertEquals(9, stats.size());
@@ -193,8 +193,8 @@
public void testCombineExisting() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 10);
- stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
- stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
+ stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
+ stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
-128L, -1L, -1);
@@ -215,12 +215,12 @@
@Test
public void testSubtractIdenticalData() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats result = after.subtract(before);
@@ -234,12 +234,12 @@
@Test
public void testSubtractIdenticalRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
final NetworkStats result = after.subtract(before);
@@ -253,13 +253,13 @@
@Test
public void testSubtractNewRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
- .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
final NetworkStats result = after.subtract(before);
@@ -275,11 +275,11 @@
@Test
public void testSubtractMissingRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
- .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
+ .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
final NetworkStats after = new NetworkStats(TEST_START, 1)
- .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
final NetworkStats result = after.subtract(before);
@@ -293,40 +293,40 @@
@Test
public void testTotalBytes() throws Exception {
final NetworkStats iface = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
assertEquals(384L, iface.getTotalBytes());
final NetworkStats uidSet = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidSet.getTotalBytes());
final NetworkStats uidTag = new NetworkStats(TEST_START, 6)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
assertEquals(64L, uidTag.getTotalBytes());
final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidMetered.getTotalBytes());
final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidRoaming.getTotalBytes());
}
@@ -343,11 +343,11 @@
@Test
public void testGroupedByIfaceAll() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
- .addEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L)
- .addEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -361,19 +361,19 @@
@Test
public void testGroupedByIface() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -390,19 +390,19 @@
@Test
public void testAddAllValues() {
final NetworkStats first = new NetworkStats(TEST_START, 5)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
final NetworkStats second = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
first.combineAllValues(second);
@@ -421,19 +421,19 @@
@Test
public void testGetTotal() {
final NetworkStats stats = new NetworkStats(TEST_START, 7)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
@@ -459,7 +459,7 @@
assertEquals(0, after.size());
// Test 1 item stats.
- before.addEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+ before.insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
after = before.clone();
after.removeUids(new int[0]);
assertEquals(1, after.size());
@@ -469,12 +469,12 @@
assertEquals(0, after.size());
// Append remaining test items.
- before.addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+ before.insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
assertEquals(7, before.size());
// Test remove with empty uid list.
@@ -505,12 +505,12 @@
@Test
public void testClone() throws Exception {
final NetworkStats original = new NetworkStats(TEST_START, 5)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
// make clone and mutate original
final NetworkStats clone = original.clone();
- original.addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
+ original.insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
assertEquals(3, original.size());
assertEquals(2, clone.size());
@@ -523,8 +523,8 @@
public void testAddWhenEmpty() throws Exception {
final NetworkStats red = new NetworkStats(TEST_START, -1);
final NetworkStats blue = new NetworkStats(TEST_START, 5)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
// We're mostly checking that we don't crash
red.combineAllValues(blue);
@@ -537,37 +537,37 @@
final String underlyingIface = "wlan0";
final int testTag1 = 8888;
NetworkStats delta = new NetworkStats(TEST_START, 17)
- .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
- .addEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
- .addEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
- .addEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
// VPN package also uses some traffic through unprotected network.
- .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
- .addEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
// Tag entries
- .addEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
- .addEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
// Irrelevant entries
- .addEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
// Underlying Iface entries
- .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
- .addEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
- .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+ .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
299L /* smaller than sum(tun0) */, 0L)
- .addEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+ .insertEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
assertEquals(20, delta.size());
@@ -635,19 +635,19 @@
final String underlyingIface = "wlan0";
NetworkStats delta = new NetworkStats(TEST_START, 9)
// 2 different apps sent/receive data via tun0.
- .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
- .addEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
// VPN package resends data through the tunnel (with exaggerated overhead)
- .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
// 1 app already has some traffic on the underlying interface, the other doesn't yet
- .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
// Traffic through the underlying interface via the vpn app.
// This test should redistribute this data correctly.
- .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
@@ -697,9 +697,9 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL);
assertEquals(3, stats.size());
@@ -724,9 +724,9 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(testUid, INTERFACES_ALL, TAG_ALL);
assertEquals(2, stats.size());
@@ -755,10 +755,10 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 4)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3)
- .addEntry(entry4);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3)
+ .insertEntry(entry4);
stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL);
assertEquals(3, stats.size());
@@ -778,8 +778,8 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(entry1)
- .addEntry(entry2);
+ .insertEntry(entry1)
+ .insertEntry(entry2);
stats.filter(UID_ALL, new String[] { }, TAG_ALL);
assertEquals(0, stats.size());
@@ -802,9 +802,9 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(UID_ALL, INTERFACES_ALL, testTag);
assertEquals(2, stats.size());
@@ -831,10 +831,10 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 4)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3)
- .addEntry(entry4);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3)
+ .insertEntry(entry4);
stats.filterDebugEntries();
@@ -891,14 +891,14 @@
0 /* operations */);
final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
- .addEntry(appEntry)
- .addEntry(xtRootUidEntry)
- .addEntry(otherEntry);
+ .insertEntry(appEntry)
+ .insertEntry(xtRootUidEntry)
+ .insertEntry(otherEntry);
final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
- .addEntry(appEntry)
- .addEntry(ebpfRootUidEntry)
- .addEntry(otherEntry);
+ .insertEntry(appEntry)
+ .insertEntry(ebpfRootUidEntry)
+ .insertEntry(otherEntry);
statsXt.apply464xlatAdjustments(stackedIface, false);
statsEbpf.apply464xlatAdjustments(stackedIface, true);
@@ -945,8 +945,8 @@
0 /* operations */);
NetworkStats stats = new NetworkStats(TEST_START, 2)
- .addEntry(firstEntry)
- .addEntry(secondEntry);
+ .insertEntry(firstEntry)
+ .insertEntry(secondEntry);
// Empty map: no adjustment
stats.apply464xlatAdjustments(new ArrayMap<>(), false);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 8c0c36b..44216e8 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,8 +23,6 @@
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
-import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
@@ -100,6 +98,7 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Matchers.anyInt;
@@ -2426,7 +2425,7 @@
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
assertTrue(testFactory.getMyStartRequested());
- testFactory.unregister();
+ testFactory.terminate();
if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback);
handlerThread.quit();
}
@@ -2452,6 +2451,38 @@
}
@Test
+ public void testNetworkFactoryUnregister() throws Exception {
+ final NetworkCapabilities filter = new NetworkCapabilities();
+ filter.clearAll();
+
+ final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
+ handlerThread.start();
+
+ // Checks that calling setScoreFilter on a NetworkFactory immediately before closing it
+ // does not crash.
+ for (int i = 0; i < 100; i++) {
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter);
+ // Register the factory and don't be surprised when the default request arrives.
+ testFactory.expectAddRequestsWithScores(0);
+ testFactory.register();
+ testFactory.waitForNetworkRequests(1);
+
+ testFactory.setScoreFilter(42);
+ testFactory.terminate();
+
+ if (i % 2 == 0) {
+ try {
+ testFactory.register();
+ fail("Re-registering terminated NetworkFactory should throw");
+ } catch (IllegalStateException expected) {
+ }
+ }
+ }
+ handlerThread.quit();
+ }
+
+ @Test
public void testNoMutableNetworkRequests() throws Exception {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
NetworkRequest request1 = new NetworkRequest.Builder()
@@ -3483,7 +3514,7 @@
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
assertLength(1, mCm.getAllNetworks());
- testFactory.unregister();
+ testFactory.terminate();
mCm.unregisterNetworkCallback(cellNetworkCallback);
handlerThread.quit();
}
@@ -3724,7 +3755,7 @@
mCm.requestNetwork(nr, networkCallback, timeoutMs);
// pass timeout and validate that UNAVAILABLE is called
- networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, null);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
// create a network satisfying request - validate that request not triggered
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -3815,7 +3846,7 @@
// Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
testFactory.triggerUnfulfillable(requests.get(newRequestId));
- networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, null);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
testFactory.waitForRequests();
// unregister network callback - a no-op (since already freed by the
@@ -3823,7 +3854,7 @@
mCm.unregisterNetworkCallback(networkCallback);
}
- testFactory.unregister();
+ testFactory.terminate();
handlerThread.quit();
}
@@ -6758,6 +6789,26 @@
}
@Test
+ public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), 0,
+ mServiceContext, null, null, mService, null, null, null, 0);
+
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ try {
+ assertFalse(
+ "Mismatched uid/package name should not pass the location permission check",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid,
+ mContext.getOpPackageName()));
+ } catch (SecurityException e) {
+ fail("checkConnectivityDiagnosticsPermissions shouldn't surface a SecurityException");
+ }
+ }
+
+ @Test
public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
final NetworkAgentInfo naiWithoutUid =
new NetworkAgentInfo(
@@ -6863,15 +6914,21 @@
}
@Test
- public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception {
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
+ throws Exception {
setUpConnectivityDiagnosticsCallback();
// Block until all other events are done processing.
HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
// Verify onConnectivityReport fired
- verify(mConnectivityDiagnosticsCallback)
- .onConnectivityReport(any(ConnectivityReport.class));
+ verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
+ argThat(report -> {
+ final NetworkCapabilities nc = report.getNetworkCapabilities();
+ return nc.getUids() == null
+ && nc.getAdministratorUids().isEmpty()
+ && nc.getOwnerUid() == Process.INVALID_UID;
+ }));
}
@Test
@@ -6886,7 +6943,13 @@
HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
// Verify onDataStallSuspected fired
- verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(any(DataStallReport.class));
+ verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(
+ argThat(report -> {
+ final NetworkCapabilities nc = report.getNetworkCapabilities();
+ return nc.getUids() == null
+ && nc.getAdministratorUids().isEmpty()
+ && nc.getOwnerUid() == Process.INVALID_UID;
+ }));
}
@Test
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index f0e5774..a6f7a36 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -240,7 +240,7 @@
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
@@ -264,14 +264,14 @@
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
+ .insertEntry(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
@@ -294,14 +294,14 @@
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
+ .insertEntry(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
BASE_BYTES + THRESHOLD_BYTES, 22L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
@@ -326,14 +326,14 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -359,14 +359,14 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -391,14 +391,14 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -424,14 +424,14 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+ .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+ .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 36deca3..b346c92 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -263,7 +263,7 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+ .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -276,7 +276,7 @@
incrementCurrentTime(DAY_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4096L, 4L, 8192L, 8L));
+ .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -306,13 +306,13 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 8L, 2048L, 16L));
+ .insertEntry(TEST_IFACE, 1024L, 8L, 2048L, 16L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
mService.setUidForeground(UID_RED, false);
mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
mService.setUidForeground(UID_RED, true);
@@ -375,7 +375,7 @@
incrementCurrentTime(2 * HOUR_IN_MILLIS);
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 512L, 4L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 512L, 4L, 512L, 4L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -415,11 +415,11 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 10);
forcePollAndWaitForIdle();
@@ -437,11 +437,11 @@
expectDefaultSettings();
states = new NetworkState[] {buildMobile3gState(IMSI_2)};
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
forcePollAndWaitForIdle();
@@ -451,12 +451,12 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
+ .insertEntry(TEST_IFACE, 2176L, 17L, 1536L, 12L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
forcePollAndWaitForIdle();
@@ -488,12 +488,13 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+ .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
- .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 4096L, 258L, 512L, 32L, 0L)
+ .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
forcePollAndWaitForIdle();
@@ -509,12 +510,13 @@
// special "removed" bucket.
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+ .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
- .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 4096L, 258L, 512L, 32L, 0L)
+ .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
final Intent intent = new Intent(ACTION_UID_REMOVED);
intent.putExtra(EXTRA_UID, UID_BLUE);
mServiceContext.sendBroadcast(intent);
@@ -546,8 +548,8 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 5);
forcePollAndWaitForIdle();
@@ -562,8 +564,8 @@
states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
forcePollAndWaitForIdle();
@@ -574,10 +576,10 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
forcePollAndWaitForIdle();
@@ -601,9 +603,9 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
forcePollAndWaitForIdle();
@@ -618,9 +620,10 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 2048L, 16L, 1024L, 8L, 0L));
forcePollAndWaitForIdle();
// first verify entire history present
@@ -664,9 +667,9 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3));
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
@@ -714,11 +717,11 @@
.thenReturn(augmentedIfaceFilter);
when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL)))
.thenReturn(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(uidStats));
+ .insertEntry(uidStats));
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
.thenReturn(new NetworkStats(getElapsedRealtime(), 2)
- .addEntry(tetheredStats1)
- .addEntry(tetheredStats2));
+ .insertEntry(tetheredStats1)
+ .insertEntry(tetheredStats2));
NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
@@ -755,8 +758,8 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
forcePollAndWaitForIdle();
@@ -770,10 +773,10 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
mService.setUidForeground(UID_RED, true);
mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
@@ -814,9 +817,9 @@
// and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
// We layer them on top by inspecting the iface properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
@@ -853,9 +856,9 @@
// ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
// on top by inspecting the iface properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
forcePollAndWaitForIdle();
@@ -888,17 +891,17 @@
// Traffic seen by kernel counters (includes software tethering).
final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1536L, 12L, 384L, 3L);
+ .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L);
// Hardware tethering traffic, not seen by kernel counters.
final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 512L, 4L, 128L, 1L);
+ .insertEntry(TEST_IFACE, 512L, 4L, 128L, 1L);
// Traffic for UID_RED.
final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
// All tethering traffic, both hardware and software.
final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
+ .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
0L);
expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
@@ -957,7 +960,7 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+ .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -972,7 +975,7 @@
incrementCurrentTime(DAY_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
+ .insertEntry(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -1026,18 +1029,18 @@
mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// Verifies that one requestStatsUpdate will be called during iface update.
- provider.expectStatsUpdate(0 /* unused */);
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
// Create some initial traffic and report to the service.
incrementCurrentTime(HOUR_IN_MILLIS);
final NetworkStats expectedStats = new NetworkStats(0L, 1)
- .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
128L, 2L, 128L, 2L, 1L))
- .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
64L, 1L, 64L, 1L, 1L));
- cb.onStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+ cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats);
// Make another empty mutable stats object. This is necessary since the new NetworkStats
// object will be used to compare with the old one in NetworkStatsRecoder, two of them
@@ -1047,8 +1050,8 @@
forcePollAndWaitForIdle();
// Verifies that one requestStatsUpdate and setAlert will be called during polling.
- provider.expectStatsUpdate(0 /* unused */);
- provider.expectSetAlert(MB_IN_BYTES);
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
+ provider.expectOnSetAlert(MB_IN_BYTES);
// Verifies that service recorded history, does not verify uid tag part.
assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
@@ -1082,13 +1085,13 @@
assertNotNull(cb);
// Simulates alert quota of the provider has been reached.
- cb.onAlertReached();
+ cb.notifyAlertReached();
HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
// Verifies that polling is triggered by alert reached.
- provider.expectStatsUpdate(0 /* unused */);
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
// Verifies that global alert will be re-armed.
- provider.expectSetAlert(MB_IN_BYTES);
+ provider.expectOnSetAlert(MB_IN_BYTES);
}
private static File getBaseDir(File statsDir) {
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 5aa32f8..bcfce66 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -366,8 +366,12 @@
});
manifest_action["instrumentation"]["meta-data"] = meta_data_action;
+ // TODO moltmann: Remove
manifest_action["feature"];
manifest_action["feature"]["inherit-from"];
+
+ manifest_action["attribution"];
+ manifest_action["attribution"]["inherit-from"];
manifest_action["original-package"];
manifest_action["overlay"];
manifest_action["protected-broadcast"];
diff --git a/wifi/Android.bp b/wifi/Android.bp
index f4d2881..70c2741 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -72,8 +72,7 @@
// classes before they are renamed.
java_library {
name: "framework-wifi-pre-jarjar",
- // TODO(b/146757305): sdk_version should be "module_lib_current"
- sdk_version: "core_current",
+ sdk_version: "module_current",
static_libs: [
"framework-wifi-util-lib",
"android.hardware.wifi-V1.0-java-constants",
@@ -83,9 +82,6 @@
"unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
"unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage
"framework-telephony-stubs",
- // TODO(b/146757305): should be unnecessary once
- // sdk_version="module_lib_current"
- "android_system_stubs_current",
],
srcs: [
":framework-wifi-updatable-sources",
@@ -98,21 +94,12 @@
"//frameworks/opt/net/wifi/service",
"//frameworks/opt/net/wifi/tests/wifitests",
],
-
- // TODO(b/146757305): should be unnecessary once
- // sdk_version="module_lib_current"
- aidl: {
- include_dirs: [
- "frameworks/base/core/java",
- ],
- },
}
// post-jarjar version of framework-wifi
java_library {
name: "framework-wifi",
- // TODO(b/146757305): sdk_version should be "module_lib_current"
- sdk_version: "core_current",
+ sdk_version: "module_current",
static_libs: [
"framework-wifi-pre-jarjar",
],
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
new file mode 100644
index 0000000..1b62ec1
--- /dev/null
+++ b/wifi/api/current.txt
@@ -0,0 +1,1199 @@
+// Signature format: 2.0
+package android.net.wifi {
+
+ public abstract class EasyConnectStatusCallback {
+ field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
+ field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
+ field public static final int EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = -10; // 0xfffffff6
+ field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
+ field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = -11; // 0xfffffff5
+ field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; // 0xfffffff4
+ field public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7; // 0xfffffff9
+ field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
+ field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
+ field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
+ field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
+ field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
+ }
+
+ public class ScanResult implements android.os.Parcelable {
+ ctor public ScanResult(@NonNull android.net.wifi.ScanResult);
+ ctor public ScanResult();
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.wifi.ScanResult.InformationElement> getInformationElements();
+ method public int getWifiStandard();
+ method public boolean is80211mcResponder();
+ method public boolean isPasspointNetwork();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public String BSSID;
+ field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3
+ field public static final int CHANNEL_WIDTH_20MHZ = 0; // 0x0
+ field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1
+ field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2
+ field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.ScanResult> CREATOR;
+ field public String SSID;
+ field public static final int WIFI_STANDARD_11AC = 5; // 0x5
+ field public static final int WIFI_STANDARD_11AX = 6; // 0x6
+ field public static final int WIFI_STANDARD_11N = 4; // 0x4
+ field public static final int WIFI_STANDARD_LEGACY = 1; // 0x1
+ field public static final int WIFI_STANDARD_UNKNOWN = 0; // 0x0
+ field public String capabilities;
+ field public int centerFreq0;
+ field public int centerFreq1;
+ field public int channelWidth;
+ field public int frequency;
+ field public int level;
+ field public CharSequence operatorFriendlyName;
+ field public long timestamp;
+ field public CharSequence venueName;
+ }
+
+ public static class ScanResult.InformationElement {
+ ctor public ScanResult.InformationElement(@NonNull android.net.wifi.ScanResult.InformationElement);
+ method @NonNull public java.nio.ByteBuffer getBytes();
+ method public int getId();
+ method public int getIdExt();
+ }
+
+ public final class SoftApConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.net.MacAddress getBssid();
+ method @Nullable public String getPassphrase();
+ method public int getSecurityType();
+ method @Nullable public String getSsid();
+ method public boolean isHiddenSsid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR;
+ field public static final int SECURITY_TYPE_OPEN = 0; // 0x0
+ field public static final int SECURITY_TYPE_WPA2_PSK = 1; // 0x1
+ field public static final int SECURITY_TYPE_WPA3_SAE = 3; // 0x3
+ field public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2; // 0x2
+ }
+
+ public enum SupplicantState implements android.os.Parcelable {
+ method public int describeContents();
+ method public static boolean isValidState(android.net.wifi.SupplicantState);
+ method public void writeToParcel(android.os.Parcel, int);
+ enum_constant public static final android.net.wifi.SupplicantState ASSOCIATED;
+ enum_constant public static final android.net.wifi.SupplicantState ASSOCIATING;
+ enum_constant public static final android.net.wifi.SupplicantState AUTHENTICATING;
+ enum_constant public static final android.net.wifi.SupplicantState COMPLETED;
+ enum_constant public static final android.net.wifi.SupplicantState DISCONNECTED;
+ enum_constant public static final android.net.wifi.SupplicantState DORMANT;
+ enum_constant public static final android.net.wifi.SupplicantState FOUR_WAY_HANDSHAKE;
+ enum_constant public static final android.net.wifi.SupplicantState GROUP_HANDSHAKE;
+ enum_constant public static final android.net.wifi.SupplicantState INACTIVE;
+ enum_constant public static final android.net.wifi.SupplicantState INTERFACE_DISABLED;
+ enum_constant public static final android.net.wifi.SupplicantState INVALID;
+ enum_constant public static final android.net.wifi.SupplicantState SCANNING;
+ enum_constant public static final android.net.wifi.SupplicantState UNINITIALIZED;
+ }
+
+ @Deprecated public class WifiConfiguration implements android.os.Parcelable {
+ ctor @Deprecated public WifiConfiguration();
+ ctor @Deprecated public WifiConfiguration(@NonNull android.net.wifi.WifiConfiguration);
+ method public int describeContents();
+ method @Deprecated public android.net.ProxyInfo getHttpProxy();
+ method @Deprecated @NonNull public String getKey();
+ method @Deprecated @NonNull public android.net.MacAddress getRandomizedMacAddress();
+ method @Deprecated public boolean isPasspoint();
+ method @Deprecated public void setHttpProxy(android.net.ProxyInfo);
+ method @Deprecated public void setSecurityParams(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated public String BSSID;
+ field @Deprecated public String FQDN;
+ field @Deprecated public static final int SECURITY_TYPE_EAP = 3; // 0x3
+ field @Deprecated public static final int SECURITY_TYPE_EAP_SUITE_B = 5; // 0x5
+ field @Deprecated public static final int SECURITY_TYPE_OPEN = 0; // 0x0
+ field @Deprecated public static final int SECURITY_TYPE_OWE = 6; // 0x6
+ field @Deprecated public static final int SECURITY_TYPE_PSK = 2; // 0x2
+ field @Deprecated public static final int SECURITY_TYPE_SAE = 4; // 0x4
+ field @Deprecated public static final int SECURITY_TYPE_WAPI_CERT = 8; // 0x8
+ field @Deprecated public static final int SECURITY_TYPE_WAPI_PSK = 7; // 0x7
+ field @Deprecated public static final int SECURITY_TYPE_WEP = 1; // 0x1
+ field @Deprecated public String SSID;
+ field @Deprecated @NonNull public java.util.BitSet allowedAuthAlgorithms;
+ field @Deprecated @NonNull public java.util.BitSet allowedGroupCiphers;
+ field @Deprecated @NonNull public java.util.BitSet allowedGroupManagementCiphers;
+ field @Deprecated @NonNull public java.util.BitSet allowedKeyManagement;
+ field @Deprecated @NonNull public java.util.BitSet allowedPairwiseCiphers;
+ field @Deprecated @NonNull public java.util.BitSet allowedProtocols;
+ field @Deprecated @NonNull public java.util.BitSet allowedSuiteBCiphers;
+ field @Deprecated public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
+ field @Deprecated public boolean hiddenSSID;
+ field @Deprecated public boolean isHomeProviderNetwork;
+ field @Deprecated public int networkId;
+ field @Deprecated public String preSharedKey;
+ field @Deprecated public int priority;
+ field @Deprecated public String providerFriendlyName;
+ field @Deprecated public long[] roamingConsortiumIds;
+ field @Deprecated public int status;
+ field @Deprecated public String[] wepKeys;
+ field @Deprecated public int wepTxKeyIndex;
+ }
+
+ @Deprecated public static class WifiConfiguration.AuthAlgorithm {
+ field @Deprecated public static final int LEAP = 2; // 0x2
+ field @Deprecated public static final int OPEN = 0; // 0x0
+ field @Deprecated public static final int SAE = 3; // 0x3
+ field @Deprecated public static final int SHARED = 1; // 0x1
+ field @Deprecated public static final String[] strings;
+ field @Deprecated public static final String varName = "auth_alg";
+ }
+
+ @Deprecated public static class WifiConfiguration.GroupCipher {
+ field @Deprecated public static final int CCMP = 3; // 0x3
+ field @Deprecated public static final int GCMP_256 = 5; // 0x5
+ field @Deprecated public static final int SMS4 = 6; // 0x6
+ field @Deprecated public static final int TKIP = 2; // 0x2
+ field @Deprecated public static final int WEP104 = 1; // 0x1
+ field @Deprecated public static final int WEP40 = 0; // 0x0
+ field @Deprecated public static final String[] strings;
+ field @Deprecated public static final String varName = "group";
+ }
+
+ @Deprecated public static class WifiConfiguration.GroupMgmtCipher {
+ field @Deprecated public static final int BIP_CMAC_256 = 0; // 0x0
+ field @Deprecated public static final int BIP_GMAC_128 = 1; // 0x1
+ field @Deprecated public static final int BIP_GMAC_256 = 2; // 0x2
+ }
+
+ @Deprecated public static class WifiConfiguration.KeyMgmt {
+ field @Deprecated public static final int IEEE8021X = 3; // 0x3
+ field @Deprecated public static final int NONE = 0; // 0x0
+ field @Deprecated public static final int OWE = 9; // 0x9
+ field @Deprecated public static final int SAE = 8; // 0x8
+ field @Deprecated public static final int SUITE_B_192 = 10; // 0xa
+ field @Deprecated public static final int WPA_EAP = 2; // 0x2
+ field @Deprecated public static final int WPA_PSK = 1; // 0x1
+ field @Deprecated public static final String[] strings;
+ field @Deprecated public static final String varName = "key_mgmt";
+ }
+
+ @Deprecated public static class WifiConfiguration.PairwiseCipher {
+ field @Deprecated public static final int CCMP = 2; // 0x2
+ field @Deprecated public static final int GCMP_256 = 3; // 0x3
+ field @Deprecated public static final int NONE = 0; // 0x0
+ field @Deprecated public static final int SMS4 = 4; // 0x4
+ field @Deprecated public static final int TKIP = 1; // 0x1
+ field @Deprecated public static final String[] strings;
+ field @Deprecated public static final String varName = "pairwise";
+ }
+
+ @Deprecated public static class WifiConfiguration.Protocol {
+ field @Deprecated public static final int RSN = 1; // 0x1
+ field @Deprecated public static final int WAPI = 3; // 0x3
+ field @Deprecated public static final int WPA = 0; // 0x0
+ field @Deprecated public static final String[] strings;
+ field @Deprecated public static final String varName = "proto";
+ }
+
+ @Deprecated public static class WifiConfiguration.Status {
+ field @Deprecated public static final int CURRENT = 0; // 0x0
+ field @Deprecated public static final int DISABLED = 1; // 0x1
+ field @Deprecated public static final int ENABLED = 2; // 0x2
+ field @Deprecated public static final String[] strings;
+ }
+
+ public class WifiEnterpriseConfig implements android.os.Parcelable {
+ ctor public WifiEnterpriseConfig();
+ ctor public WifiEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+ method public int describeContents();
+ method public String getAltSubjectMatch();
+ method public String getAnonymousIdentity();
+ method @Nullable public java.security.cert.X509Certificate getCaCertificate();
+ method @Nullable public java.security.cert.X509Certificate[] getCaCertificates();
+ method public java.security.cert.X509Certificate getClientCertificate();
+ method @Nullable public java.security.cert.X509Certificate[] getClientCertificateChain();
+ method @Nullable public java.security.PrivateKey getClientPrivateKey();
+ method public String getDomainSuffixMatch();
+ method public int getEapMethod();
+ method public String getIdentity();
+ method public String getPassword();
+ method public int getPhase2Method();
+ method public String getPlmn();
+ method public String getRealm();
+ method @Deprecated public String getSubjectMatch();
+ method public boolean isAuthenticationSimBased();
+ method public void setAltSubjectMatch(String);
+ method public void setAnonymousIdentity(String);
+ method public void setCaCertificate(@Nullable java.security.cert.X509Certificate);
+ method public void setCaCertificates(@Nullable java.security.cert.X509Certificate[]);
+ method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+ method public void setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]);
+ method public void setDomainSuffixMatch(String);
+ method public void setEapMethod(int);
+ method public void setIdentity(String);
+ method public void setPassword(String);
+ method public void setPhase2Method(int);
+ method public void setPlmn(String);
+ method public void setRealm(String);
+ method @Deprecated public void setSubjectMatch(String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiEnterpriseConfig> CREATOR;
+ field public static final String EXTRA_WAPI_AS_CERTIFICATE_DATA = "android.net.wifi.extra.WAPI_AS_CERTIFICATE_DATA";
+ field public static final String EXTRA_WAPI_AS_CERTIFICATE_NAME = "android.net.wifi.extra.WAPI_AS_CERTIFICATE_NAME";
+ field public static final String EXTRA_WAPI_USER_CERTIFICATE_DATA = "android.net.wifi.extra.WAPI_USER_CERTIFICATE_DATA";
+ field public static final String EXTRA_WAPI_USER_CERTIFICATE_NAME = "android.net.wifi.extra.WAPI_USER_CERTIFICATE_NAME";
+ field public static final String WAPI_AS_CERTIFICATE = "WAPIAS_";
+ field public static final String WAPI_USER_CERTIFICATE = "WAPIUSR_";
+ }
+
+ public static final class WifiEnterpriseConfig.Eap {
+ field public static final int AKA = 5; // 0x5
+ field public static final int AKA_PRIME = 6; // 0x6
+ field public static final int NONE = -1; // 0xffffffff
+ field public static final int PEAP = 0; // 0x0
+ field public static final int PWD = 3; // 0x3
+ field public static final int SIM = 4; // 0x4
+ field public static final int TLS = 1; // 0x1
+ field public static final int TTLS = 2; // 0x2
+ field public static final int UNAUTH_TLS = 7; // 0x7
+ field public static final int WAPI_CERT = 8; // 0x8
+ }
+
+ public static final class WifiEnterpriseConfig.Phase2 {
+ field public static final int AKA = 6; // 0x6
+ field public static final int AKA_PRIME = 7; // 0x7
+ field public static final int GTC = 4; // 0x4
+ field public static final int MSCHAP = 2; // 0x2
+ field public static final int MSCHAPV2 = 3; // 0x3
+ field public static final int NONE = 0; // 0x0
+ field public static final int PAP = 1; // 0x1
+ field public static final int SIM = 5; // 0x5
+ }
+
+ public class WifiInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public String getBSSID();
+ method public static android.net.NetworkInfo.DetailedState getDetailedStateOf(android.net.wifi.SupplicantState);
+ method public int getFrequency();
+ method public boolean getHiddenSSID();
+ method public int getIpAddress();
+ method public int getLinkSpeed();
+ method public String getMacAddress();
+ method public int getMaxSupportedRxLinkSpeedMbps();
+ method public int getMaxSupportedTxLinkSpeedMbps();
+ method public int getNetworkId();
+ method @Nullable public String getPasspointFqdn();
+ method @Nullable public String getPasspointProviderFriendlyName();
+ method public int getRssi();
+ method @IntRange(from=0xffffffff) public int getRxLinkSpeedMbps();
+ method public String getSSID();
+ method public android.net.wifi.SupplicantState getSupplicantState();
+ method @IntRange(from=0xffffffff) public int getTxLinkSpeedMbps();
+ method public int getWifiStandard();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final String FREQUENCY_UNITS = "MHz";
+ field public static final String LINK_SPEED_UNITS = "Mbps";
+ field public static final int LINK_SPEED_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public static final class WifiInfo.Builder {
+ ctor public WifiInfo.Builder();
+ method @NonNull public android.net.wifi.WifiInfo build();
+ method @NonNull public android.net.wifi.WifiInfo.Builder setBssid(@NonNull String);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setNetworkId(int);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setRssi(int);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setSsid(@NonNull byte[]);
+ }
+
+ public class WifiManager {
+ method @Deprecated public int addNetwork(android.net.wifi.WifiConfiguration);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int addNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+ method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public void addSuggestionConnectionStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener);
+ method @Deprecated public static int calculateSignalLevel(int, int);
+ method @IntRange(from=0) public int calculateSignalLevel(int);
+ method @Deprecated public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
+ method public static int compareSignalLevel(int, int);
+ method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(String);
+ method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, String);
+ method @Deprecated public android.net.wifi.WifiManager.WifiLock createWifiLock(String);
+ method @Deprecated public boolean disableNetwork(int);
+ method @Deprecated public boolean disconnect();
+ method @Deprecated public boolean enableNetwork(int, boolean);
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
+ method public android.net.wifi.WifiInfo getConnectionInfo();
+ method public android.net.DhcpInfo getDhcpInfo();
+ method public int getMaxNumberOfNetworkSuggestionsPerApp();
+ method @IntRange(from=0) public int getMaxSignalLevel();
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public java.util.List<android.net.wifi.WifiNetworkSuggestion> getNetworkSuggestions();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
+ method public java.util.List<android.net.wifi.ScanResult> getScanResults();
+ method public int getWifiState();
+ method public boolean is5GHzBandSupported();
+ method public boolean is6GHzBandSupported();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled();
+ method @Deprecated public boolean isDeviceToApRttSupported();
+ method public boolean isEasyConnectSupported();
+ method public boolean isEnhancedOpenSupported();
+ method public boolean isEnhancedPowerReportingSupported();
+ method public boolean isP2pSupported();
+ method public boolean isPreferredNetworkOffloadSupported();
+ method @Deprecated public boolean isScanAlwaysAvailable();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isScanThrottleEnabled();
+ method public boolean isStaApConcurrencySupported();
+ method public boolean isTdlsSupported();
+ method public boolean isWapiSupported();
+ method public boolean isWifiEnabled();
+ method public boolean isWifiStandardSupported(int);
+ method public boolean isWpa3SaeSupported();
+ method public boolean isWpa3SuiteBSupported();
+ method @Deprecated public boolean pingSupplicant();
+ method @Deprecated public boolean reassociate();
+ method @Deprecated public boolean reconnect();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void registerScanResultsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.ScanResultsCallback);
+ method @Deprecated public boolean removeNetwork(int);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int removeNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_CARRIER_PROVISIONING}) public void removePasspointConfiguration(String);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeSuggestionConnectionStatusListener(@NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener);
+ method @Deprecated public boolean saveConfiguration();
+ method public void setTdlsEnabled(java.net.InetAddress, boolean);
+ method public void setTdlsEnabledWithMacAddress(String, boolean);
+ method @Deprecated public boolean setWifiEnabled(boolean);
+ method @RequiresPermission(allOf={android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler);
+ method @Deprecated public boolean startScan();
+ method @Deprecated public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void unregisterScanResultsCallback(@NonNull android.net.wifi.WifiManager.ScanResultsCallback);
+ method @Deprecated public int updateNetwork(android.net.wifi.WifiConfiguration);
+ field public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
+ field public static final String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
+ field public static final String ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION";
+ field public static final String ACTION_WIFI_SCAN_AVAILABILITY_CHANGED = "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED";
+ field @Deprecated public static final int ERROR_AUTHENTICATING = 1; // 0x1
+ field @Deprecated public static final String EXTRA_BSSID = "bssid";
+ field public static final String EXTRA_NETWORK_INFO = "networkInfo";
+ field public static final String EXTRA_NETWORK_SUGGESTION = "android.net.wifi.extra.NETWORK_SUGGESTION";
+ field public static final String EXTRA_NEW_RSSI = "newRssi";
+ field @Deprecated public static final String EXTRA_NEW_STATE = "newState";
+ field public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
+ field public static final String EXTRA_RESULTS_UPDATED = "resultsUpdated";
+ field public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE";
+ field @Deprecated public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
+ field @Deprecated public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
+ field @Deprecated public static final String EXTRA_WIFI_INFO = "wifiInfo";
+ field public static final String EXTRA_WIFI_STATE = "wifi_state";
+ field public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
+ field public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
+ field public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
+ field public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; // 0x3
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; // 0x4
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID = 7; // 0x7
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED = 6; // 0x6
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2; // 0x2
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
+ field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 5; // 0x5
+ field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0
+ field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_ASSOCIATION = 1; // 0x1
+ field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION = 2; // 0x2
+ field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_IP_PROVISIONING = 3; // 0x3
+ field public static final int STATUS_SUGGESTION_CONNECTION_FAILURE_UNKNOWN = 0; // 0x0
+ field @Deprecated public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE";
+ field @Deprecated public static final String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE";
+ field public static final String UNKNOWN_SSID = "<unknown ssid>";
+ field @Deprecated public static final int WIFI_MODE_FULL = 1; // 0x1
+ field public static final int WIFI_MODE_FULL_HIGH_PERF = 3; // 0x3
+ field public static final int WIFI_MODE_FULL_LOW_LATENCY = 4; // 0x4
+ field @Deprecated public static final int WIFI_MODE_SCAN_ONLY = 2; // 0x2
+ field public static final String WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED";
+ field public static final int WIFI_STATE_DISABLED = 1; // 0x1
+ field public static final int WIFI_STATE_DISABLING = 0; // 0x0
+ field public static final int WIFI_STATE_ENABLED = 3; // 0x3
+ field public static final int WIFI_STATE_ENABLING = 2; // 0x2
+ field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4
+ field @Deprecated public static final int WPS_AUTH_FAILURE = 6; // 0x6
+ field @Deprecated public static final int WPS_OVERLAP_ERROR = 3; // 0x3
+ field @Deprecated public static final int WPS_TIMED_OUT = 7; // 0x7
+ field @Deprecated public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
+ field @Deprecated public static final int WPS_WEP_PROHIBITED = 4; // 0x4
+ }
+
+ public static class WifiManager.LocalOnlyHotspotCallback {
+ ctor public WifiManager.LocalOnlyHotspotCallback();
+ method public void onFailed(int);
+ method public void onStarted(android.net.wifi.WifiManager.LocalOnlyHotspotReservation);
+ method public void onStopped();
+ field public static final int ERROR_GENERIC = 2; // 0x2
+ field public static final int ERROR_INCOMPATIBLE_MODE = 3; // 0x3
+ field public static final int ERROR_NO_CHANNEL = 1; // 0x1
+ field public static final int ERROR_TETHERING_DISALLOWED = 4; // 0x4
+ }
+
+ public class WifiManager.LocalOnlyHotspotReservation implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
+ method @Deprecated @Nullable public android.net.wifi.WifiConfiguration getWifiConfiguration();
+ }
+
+ public class WifiManager.MulticastLock {
+ method public void acquire();
+ method public boolean isHeld();
+ method public void release();
+ method public void setReferenceCounted(boolean);
+ }
+
+ public abstract static class WifiManager.ScanResultsCallback {
+ ctor public WifiManager.ScanResultsCallback();
+ method public abstract void onScanResultsAvailable();
+ }
+
+ public static interface WifiManager.SuggestionConnectionStatusListener {
+ method public void onConnectionStatus(@NonNull android.net.wifi.WifiNetworkSuggestion, int);
+ }
+
+ public class WifiManager.WifiLock {
+ method public void acquire();
+ method public boolean isHeld();
+ method public void release();
+ method public void setReferenceCounted(boolean);
+ method public void setWorkSource(android.os.WorkSource);
+ }
+
+ @Deprecated public abstract static class WifiManager.WpsCallback {
+ ctor @Deprecated public WifiManager.WpsCallback();
+ method @Deprecated public abstract void onFailed(int);
+ method @Deprecated public abstract void onStarted(String);
+ method @Deprecated public abstract void onSucceeded();
+ }
+
+ public final class WifiNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkSpecifier> CREATOR;
+ }
+
+ public static final class WifiNetworkSpecifier.Builder {
+ ctor public WifiNetworkSpecifier.Builder();
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier build();
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setBssid(@NonNull android.net.MacAddress);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setBssidPattern(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setIsEnhancedOpen(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setIsHiddenSsid(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setSsid(@NonNull String);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setSsidPattern(@NonNull android.os.PatternMatcher);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa2Passphrase(@NonNull String);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3Passphrase(@NonNull String);
+ }
+
+ public final class WifiNetworkSuggestion implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.net.MacAddress getBssid();
+ method @Nullable public android.net.wifi.WifiEnterpriseConfig getEnterpriseConfig();
+ method @Nullable public String getPassphrase();
+ method @Nullable public android.net.wifi.hotspot2.PasspointConfiguration getPasspointConfig();
+ method @IntRange(from=0) public int getPriority();
+ method @Nullable public String getSsid();
+ method public boolean isAppInteractionRequired();
+ method public boolean isCredentialSharedWithUser();
+ method public boolean isEnhancedOpen();
+ method public boolean isHiddenSsid();
+ method public boolean isInitialAutojoinEnabled();
+ method public boolean isMetered();
+ method public boolean isUntrusted();
+ method public boolean isUserInteractionRequired();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkSuggestion> CREATOR;
+ }
+
+ public static final class WifiNetworkSuggestion.Builder {
+ ctor public WifiNetworkSuggestion.Builder();
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion build();
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setBssid(@NonNull android.net.MacAddress);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setCredentialSharedWithUser(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsAppInteractionRequired(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsInitialAutojoinEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsMetered(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSsid(@NonNull String);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setUntrusted(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiEnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiPassphrase(@NonNull String);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2Passphrase(@NonNull String);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3Passphrase(@NonNull String);
+ }
+
+ public class WpsInfo implements android.os.Parcelable {
+ ctor public WpsInfo();
+ ctor public WpsInfo(android.net.wifi.WpsInfo);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public String BSSID;
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WpsInfo> CREATOR;
+ field public static final int DISPLAY = 1; // 0x1
+ field public static final int INVALID = 4; // 0x4
+ field public static final int KEYPAD = 2; // 0x2
+ field public static final int LABEL = 3; // 0x3
+ field public static final int PBC = 0; // 0x0
+ field public String pin;
+ field public int setup;
+ }
+
+}
+
+package android.net.wifi.aware {
+
+ public class AttachCallback {
+ ctor public AttachCallback();
+ method public void onAttachFailed();
+ method public void onAttached(android.net.wifi.aware.WifiAwareSession);
+ }
+
+ public final class Characteristics implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getMaxMatchFilterLength();
+ method public int getMaxServiceNameLength();
+ method public int getMaxServiceSpecificInfoLength();
+ method public int getSupportedCipherSuites();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.Characteristics> CREATOR;
+ field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1; // 0x1
+ field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_256 = 2; // 0x2
+ }
+
+ public class DiscoverySession implements java.lang.AutoCloseable {
+ method public void close();
+ method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierOpen(@NonNull android.net.wifi.aware.PeerHandle);
+ method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(@NonNull android.net.wifi.aware.PeerHandle, @NonNull String);
+ method public void sendMessage(@NonNull android.net.wifi.aware.PeerHandle, int, @Nullable byte[]);
+ }
+
+ public class DiscoverySessionCallback {
+ ctor public DiscoverySessionCallback();
+ method public void onMessageReceived(android.net.wifi.aware.PeerHandle, byte[]);
+ method public void onMessageSendFailed(int);
+ method public void onMessageSendSucceeded(int);
+ method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
+ method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
+ method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
+ method public void onSessionConfigFailed();
+ method public void onSessionConfigUpdated();
+ method public void onSessionTerminated();
+ method public void onSubscribeStarted(@NonNull android.net.wifi.aware.SubscribeDiscoverySession);
+ }
+
+ public class IdentityChangedListener {
+ ctor public IdentityChangedListener();
+ method public void onIdentityChanged(byte[]);
+ }
+
+ public final class ParcelablePeerHandle extends android.net.wifi.aware.PeerHandle implements android.os.Parcelable {
+ ctor public ParcelablePeerHandle(@NonNull android.net.wifi.aware.PeerHandle);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.ParcelablePeerHandle> CREATOR;
+ }
+
+ public class PeerHandle {
+ }
+
+ public final class PublishConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.PublishConfig> CREATOR;
+ field public static final int PUBLISH_TYPE_SOLICITED = 1; // 0x1
+ field public static final int PUBLISH_TYPE_UNSOLICITED = 0; // 0x0
+ }
+
+ public static final class PublishConfig.Builder {
+ ctor public PublishConfig.Builder();
+ method public android.net.wifi.aware.PublishConfig build();
+ method public android.net.wifi.aware.PublishConfig.Builder setMatchFilter(@Nullable java.util.List<byte[]>);
+ method public android.net.wifi.aware.PublishConfig.Builder setPublishType(int);
+ method public android.net.wifi.aware.PublishConfig.Builder setRangingEnabled(boolean);
+ method public android.net.wifi.aware.PublishConfig.Builder setServiceName(@NonNull String);
+ method public android.net.wifi.aware.PublishConfig.Builder setServiceSpecificInfo(@Nullable byte[]);
+ method public android.net.wifi.aware.PublishConfig.Builder setTerminateNotificationEnabled(boolean);
+ method public android.net.wifi.aware.PublishConfig.Builder setTtlSec(int);
+ }
+
+ public class PublishDiscoverySession extends android.net.wifi.aware.DiscoverySession {
+ method public void updatePublish(@NonNull android.net.wifi.aware.PublishConfig);
+ }
+
+ public final class SubscribeConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.SubscribeConfig> CREATOR;
+ field public static final int SUBSCRIBE_TYPE_ACTIVE = 1; // 0x1
+ field public static final int SUBSCRIBE_TYPE_PASSIVE = 0; // 0x0
+ }
+
+ public static final class SubscribeConfig.Builder {
+ ctor public SubscribeConfig.Builder();
+ method public android.net.wifi.aware.SubscribeConfig build();
+ method public android.net.wifi.aware.SubscribeConfig.Builder setMatchFilter(@Nullable java.util.List<byte[]>);
+ method public android.net.wifi.aware.SubscribeConfig.Builder setMaxDistanceMm(int);
+ method public android.net.wifi.aware.SubscribeConfig.Builder setMinDistanceMm(int);
+ method public android.net.wifi.aware.SubscribeConfig.Builder setServiceName(@NonNull String);
+ method public android.net.wifi.aware.SubscribeConfig.Builder setServiceSpecificInfo(@Nullable byte[]);
+ method public android.net.wifi.aware.SubscribeConfig.Builder setSubscribeType(int);
+ method public android.net.wifi.aware.SubscribeConfig.Builder setTerminateNotificationEnabled(boolean);
+ method public android.net.wifi.aware.SubscribeConfig.Builder setTtlSec(int);
+ }
+
+ public class SubscribeDiscoverySession extends android.net.wifi.aware.DiscoverySession {
+ method public void updateSubscribe(@NonNull android.net.wifi.aware.SubscribeConfig);
+ }
+
+ public class WifiAwareManager {
+ method public void attach(@NonNull android.net.wifi.aware.AttachCallback, @Nullable android.os.Handler);
+ method public void attach(@NonNull android.net.wifi.aware.AttachCallback, @NonNull android.net.wifi.aware.IdentityChangedListener, @Nullable android.os.Handler);
+ method public android.net.wifi.aware.Characteristics getCharacteristics();
+ method public boolean isAvailable();
+ field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
+ field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
+ field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
+ }
+
+ public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
+ method public int describeContents();
+ method @Nullable public java.net.Inet6Address getPeerIpv6Addr();
+ method public int getPort();
+ method public int getTransportProtocol();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.WifiAwareNetworkInfo> CREATOR;
+ }
+
+ public final class WifiAwareNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.WifiAwareNetworkSpecifier> CREATOR;
+ }
+
+ public static final class WifiAwareNetworkSpecifier.Builder {
+ ctor public WifiAwareNetworkSpecifier.Builder(@NonNull android.net.wifi.aware.DiscoverySession, @NonNull android.net.wifi.aware.PeerHandle);
+ method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier build();
+ method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPmk(@NonNull byte[]);
+ method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPort(@IntRange(from=0, to=65535) int);
+ method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPskPassphrase(@NonNull String);
+ method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setTransportProtocol(@IntRange(from=0, to=255) int);
+ }
+
+ public class WifiAwareSession implements java.lang.AutoCloseable {
+ method public void close();
+ method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, @NonNull byte[]);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, @NonNull byte[], @NonNull String);
+ method public void publish(@NonNull android.net.wifi.aware.PublishConfig, @NonNull android.net.wifi.aware.DiscoverySessionCallback, @Nullable android.os.Handler);
+ method public void subscribe(@NonNull android.net.wifi.aware.SubscribeConfig, @NonNull android.net.wifi.aware.DiscoverySessionCallback, @Nullable android.os.Handler);
+ }
+
+}
+
+package android.net.wifi.hotspot2 {
+
+ public final class ConfigParser {
+ method public static android.net.wifi.hotspot2.PasspointConfiguration parsePasspointConfig(String, byte[]);
+ }
+
+ public final class PasspointConfiguration implements android.os.Parcelable {
+ ctor public PasspointConfiguration();
+ ctor public PasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
+ method public int describeContents();
+ method public android.net.wifi.hotspot2.pps.Credential getCredential();
+ method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp();
+ method public long getSubscriptionExpirationTimeMillis();
+ method @NonNull public String getUniqueId();
+ method public boolean isOsuProvisioned();
+ method public void setCredential(android.net.wifi.hotspot2.pps.Credential);
+ method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.PasspointConfiguration> CREATOR;
+ }
+
+}
+
+package android.net.wifi.hotspot2.omadm {
+
+ public final class PpsMoParser {
+ method public static android.net.wifi.hotspot2.PasspointConfiguration parseMoText(String);
+ }
+
+}
+
+package android.net.wifi.hotspot2.pps {
+
+ public final class Credential implements android.os.Parcelable {
+ ctor public Credential();
+ ctor public Credential(android.net.wifi.hotspot2.pps.Credential);
+ method public int describeContents();
+ method public java.security.cert.X509Certificate getCaCertificate();
+ method public android.net.wifi.hotspot2.pps.Credential.CertificateCredential getCertCredential();
+ method public java.security.cert.X509Certificate[] getClientCertificateChain();
+ method public java.security.PrivateKey getClientPrivateKey();
+ method public String getRealm();
+ method public android.net.wifi.hotspot2.pps.Credential.SimCredential getSimCredential();
+ method public android.net.wifi.hotspot2.pps.Credential.UserCredential getUserCredential();
+ method public void setCaCertificate(java.security.cert.X509Certificate);
+ method public void setCertCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential);
+ method public void setClientCertificateChain(java.security.cert.X509Certificate[]);
+ method public void setClientPrivateKey(java.security.PrivateKey);
+ method public void setRealm(String);
+ method public void setSimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential);
+ method public void setUserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential> CREATOR;
+ }
+
+ public static final class Credential.CertificateCredential implements android.os.Parcelable {
+ ctor public Credential.CertificateCredential();
+ ctor public Credential.CertificateCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential);
+ method public int describeContents();
+ method public byte[] getCertSha256Fingerprint();
+ method public String getCertType();
+ method public void setCertSha256Fingerprint(byte[]);
+ method public void setCertType(String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.CertificateCredential> CREATOR;
+ }
+
+ public static final class Credential.SimCredential implements android.os.Parcelable {
+ ctor public Credential.SimCredential();
+ ctor public Credential.SimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential);
+ method public int describeContents();
+ method public int getEapType();
+ method public String getImsi();
+ method public void setEapType(int);
+ method public void setImsi(String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.SimCredential> CREATOR;
+ }
+
+ public static final class Credential.UserCredential implements android.os.Parcelable {
+ ctor public Credential.UserCredential();
+ ctor public Credential.UserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential);
+ method public int describeContents();
+ method public int getEapType();
+ method public String getNonEapInnerMethod();
+ method public String getPassword();
+ method public String getUsername();
+ method public void setEapType(int);
+ method public void setNonEapInnerMethod(String);
+ method public void setPassword(String);
+ method public void setUsername(String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.UserCredential> CREATOR;
+ }
+
+ public final class HomeSp implements android.os.Parcelable {
+ ctor public HomeSp();
+ ctor public HomeSp(android.net.wifi.hotspot2.pps.HomeSp);
+ method public int describeContents();
+ method public String getFqdn();
+ method public String getFriendlyName();
+ method public long[] getRoamingConsortiumOis();
+ method public void setFqdn(String);
+ method public void setFriendlyName(String);
+ method public void setRoamingConsortiumOis(long[]);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
+ }
+
+}
+
+package android.net.wifi.p2p {
+
+ public class WifiP2pConfig implements android.os.Parcelable {
+ ctor public WifiP2pConfig();
+ ctor public WifiP2pConfig(android.net.wifi.p2p.WifiP2pConfig);
+ method public int describeContents();
+ method public int getGroupOwnerBand();
+ method public int getNetworkId();
+ method @Nullable public String getNetworkName();
+ method @Nullable public String getPassphrase();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pConfig> CREATOR;
+ field public static final int GROUP_OWNER_BAND_2GHZ = 1; // 0x1
+ field public static final int GROUP_OWNER_BAND_5GHZ = 2; // 0x2
+ field public static final int GROUP_OWNER_BAND_AUTO = 0; // 0x0
+ field public static final int GROUP_OWNER_INTENT_AUTO = -1; // 0xffffffff
+ field public static final int GROUP_OWNER_INTENT_MAX = 15; // 0xf
+ field public static final int GROUP_OWNER_INTENT_MIN = 0; // 0x0
+ field public String deviceAddress;
+ field @IntRange(from=0, to=15) public int groupOwnerIntent;
+ field public android.net.wifi.WpsInfo wps;
+ }
+
+ public static final class WifiP2pConfig.Builder {
+ ctor public WifiP2pConfig.Builder();
+ method @NonNull public android.net.wifi.p2p.WifiP2pConfig build();
+ method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean);
+ method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(@Nullable android.net.MacAddress);
+ method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOperatingBand(int);
+ method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOperatingFrequency(int);
+ method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(@NonNull String);
+ method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(@NonNull String);
+ }
+
+ public class WifiP2pDevice implements android.os.Parcelable {
+ ctor public WifiP2pDevice();
+ ctor public WifiP2pDevice(android.net.wifi.p2p.WifiP2pDevice);
+ method public int describeContents();
+ method @Nullable public android.net.wifi.p2p.WifiP2pWfdInfo getWfdInfo();
+ method public boolean isGroupOwner();
+ method public boolean isServiceDiscoveryCapable();
+ method public void update(@NonNull android.net.wifi.p2p.WifiP2pDevice);
+ method public boolean wpsDisplaySupported();
+ method public boolean wpsKeypadSupported();
+ method public boolean wpsPbcSupported();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int AVAILABLE = 3; // 0x3
+ field public static final int CONNECTED = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pDevice> CREATOR;
+ field public static final int FAILED = 2; // 0x2
+ field public static final int INVITED = 1; // 0x1
+ field public static final int UNAVAILABLE = 4; // 0x4
+ field public String deviceAddress;
+ field public String deviceName;
+ field public String primaryDeviceType;
+ field public String secondaryDeviceType;
+ field public int status;
+ }
+
+ public class WifiP2pDeviceList implements android.os.Parcelable {
+ ctor public WifiP2pDeviceList();
+ ctor public WifiP2pDeviceList(android.net.wifi.p2p.WifiP2pDeviceList);
+ method public int describeContents();
+ method public android.net.wifi.p2p.WifiP2pDevice get(String);
+ method public java.util.Collection<android.net.wifi.p2p.WifiP2pDevice> getDeviceList();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pDeviceList> CREATOR;
+ }
+
+ public class WifiP2pGroup implements android.os.Parcelable {
+ ctor public WifiP2pGroup();
+ ctor public WifiP2pGroup(android.net.wifi.p2p.WifiP2pGroup);
+ method public int describeContents();
+ method public java.util.Collection<android.net.wifi.p2p.WifiP2pDevice> getClientList();
+ method public int getFrequency();
+ method public String getInterface();
+ method public int getNetworkId();
+ method public String getNetworkName();
+ method public android.net.wifi.p2p.WifiP2pDevice getOwner();
+ method public String getPassphrase();
+ method public boolean isGroupOwner();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroup> CREATOR;
+ field public static final int NETWORK_ID_PERSISTENT = -2; // 0xfffffffe
+ field public static final int NETWORK_ID_TEMPORARY = -1; // 0xffffffff
+ }
+
+ public class WifiP2pInfo implements android.os.Parcelable {
+ ctor public WifiP2pInfo();
+ ctor public WifiP2pInfo(android.net.wifi.p2p.WifiP2pInfo);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pInfo> CREATOR;
+ field public boolean groupFormed;
+ field public java.net.InetAddress groupOwnerAddress;
+ field public boolean isGroupOwner;
+ }
+
+ public class WifiP2pManager {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void addLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void addServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void cancelConnect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void clearLocalServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void clearServiceRequests(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void createGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pConfig, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public android.net.wifi.p2p.WifiP2pManager.Channel initialize(android.content.Context, android.os.Looper, android.net.wifi.p2p.WifiP2pManager.ChannelListener);
+ method public void removeGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void removeLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestDeviceInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.DeviceInfoListener);
+ method public void requestDiscoveryState(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener);
+ method public void requestNetworkInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener);
+ method public void requestP2pState(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.P2pStateListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener);
+ method public void setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener);
+ method public void setServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ServiceResponseListener);
+ method public void setUpnpServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.UpnpServiceResponseListener);
+ method public void stopPeerDiscovery(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ field public static final int BUSY = 2; // 0x2
+ field public static final int ERROR = 0; // 0x0
+ field public static final String EXTRA_DISCOVERY_STATE = "discoveryState";
+ field public static final String EXTRA_NETWORK_INFO = "networkInfo";
+ field public static final String EXTRA_P2P_DEVICE_LIST = "wifiP2pDeviceList";
+ field public static final String EXTRA_WIFI_P2P_DEVICE = "wifiP2pDevice";
+ field public static final String EXTRA_WIFI_P2P_GROUP = "p2pGroupInfo";
+ field public static final String EXTRA_WIFI_P2P_INFO = "wifiP2pInfo";
+ field public static final String EXTRA_WIFI_STATE = "wifi_p2p_state";
+ field public static final int NO_SERVICE_REQUESTS = 3; // 0x3
+ field public static final int P2P_UNSUPPORTED = 1; // 0x1
+ field public static final String WIFI_P2P_CONNECTION_CHANGED_ACTION = "android.net.wifi.p2p.CONNECTION_STATE_CHANGE";
+ field public static final String WIFI_P2P_DISCOVERY_CHANGED_ACTION = "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE";
+ field public static final int WIFI_P2P_DISCOVERY_STARTED = 2; // 0x2
+ field public static final int WIFI_P2P_DISCOVERY_STOPPED = 1; // 0x1
+ field public static final String WIFI_P2P_PEERS_CHANGED_ACTION = "android.net.wifi.p2p.PEERS_CHANGED";
+ field public static final String WIFI_P2P_STATE_CHANGED_ACTION = "android.net.wifi.p2p.STATE_CHANGED";
+ field public static final int WIFI_P2P_STATE_DISABLED = 1; // 0x1
+ field public static final int WIFI_P2P_STATE_ENABLED = 2; // 0x2
+ field public static final String WIFI_P2P_THIS_DEVICE_CHANGED_ACTION = "android.net.wifi.p2p.THIS_DEVICE_CHANGED";
+ }
+
+ public static interface WifiP2pManager.ActionListener {
+ method public void onFailure(int);
+ method public void onSuccess();
+ }
+
+ public static class WifiP2pManager.Channel implements java.lang.AutoCloseable {
+ method public void close();
+ }
+
+ public static interface WifiP2pManager.ChannelListener {
+ method public void onChannelDisconnected();
+ }
+
+ public static interface WifiP2pManager.ConnectionInfoListener {
+ method public void onConnectionInfoAvailable(android.net.wifi.p2p.WifiP2pInfo);
+ }
+
+ public static interface WifiP2pManager.DeviceInfoListener {
+ method public void onDeviceInfoAvailable(@Nullable android.net.wifi.p2p.WifiP2pDevice);
+ }
+
+ public static interface WifiP2pManager.DiscoveryStateListener {
+ method public void onDiscoveryStateAvailable(int);
+ }
+
+ public static interface WifiP2pManager.DnsSdServiceResponseListener {
+ method public void onDnsSdServiceAvailable(String, String, android.net.wifi.p2p.WifiP2pDevice);
+ }
+
+ public static interface WifiP2pManager.DnsSdTxtRecordListener {
+ method public void onDnsSdTxtRecordAvailable(String, java.util.Map<java.lang.String,java.lang.String>, android.net.wifi.p2p.WifiP2pDevice);
+ }
+
+ public static interface WifiP2pManager.GroupInfoListener {
+ method public void onGroupInfoAvailable(android.net.wifi.p2p.WifiP2pGroup);
+ }
+
+ public static interface WifiP2pManager.NetworkInfoListener {
+ method public void onNetworkInfoAvailable(@NonNull android.net.NetworkInfo);
+ }
+
+ public static interface WifiP2pManager.P2pStateListener {
+ method public void onP2pStateAvailable(int);
+ }
+
+ public static interface WifiP2pManager.PeerListListener {
+ method public void onPeersAvailable(android.net.wifi.p2p.WifiP2pDeviceList);
+ }
+
+ public static interface WifiP2pManager.ServiceResponseListener {
+ method public void onServiceAvailable(int, byte[], android.net.wifi.p2p.WifiP2pDevice);
+ }
+
+ public static interface WifiP2pManager.UpnpServiceResponseListener {
+ method public void onUpnpServiceAvailable(java.util.List<java.lang.String>, android.net.wifi.p2p.WifiP2pDevice);
+ }
+
+ public final class WifiP2pWfdInfo implements android.os.Parcelable {
+ ctor public WifiP2pWfdInfo();
+ ctor public WifiP2pWfdInfo(@Nullable android.net.wifi.p2p.WifiP2pWfdInfo);
+ method public int describeContents();
+ method public int getControlPort();
+ method public int getDeviceType();
+ method public int getMaxThroughput();
+ method public boolean isContentProtectionSupported();
+ method public boolean isEnabled();
+ method public boolean isSessionAvailable();
+ method public void setContentProtectionSupported(boolean);
+ method public void setControlPort(@IntRange(from=0) int);
+ method public boolean setDeviceType(int);
+ method public void setEnabled(boolean);
+ method public void setMaxThroughput(@IntRange(from=0) int);
+ method public void setSessionAvailable(boolean);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pWfdInfo> CREATOR;
+ field public static final int DEVICE_TYPE_PRIMARY_SINK = 1; // 0x1
+ field public static final int DEVICE_TYPE_SECONDARY_SINK = 2; // 0x2
+ field public static final int DEVICE_TYPE_SOURCE_OR_PRIMARY_SINK = 3; // 0x3
+ field public static final int DEVICE_TYPE_WFD_SOURCE = 0; // 0x0
+ }
+
+}
+
+package android.net.wifi.p2p.nsd {
+
+ public class WifiP2pDnsSdServiceInfo extends android.net.wifi.p2p.nsd.WifiP2pServiceInfo {
+ method public static android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo newInstance(String, String, java.util.Map<java.lang.String,java.lang.String>);
+ }
+
+ public class WifiP2pDnsSdServiceRequest extends android.net.wifi.p2p.nsd.WifiP2pServiceRequest {
+ method public static android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest newInstance();
+ method public static android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest newInstance(String);
+ method public static android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest newInstance(String, String);
+ }
+
+ public class WifiP2pServiceInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int SERVICE_TYPE_ALL = 0; // 0x0
+ field public static final int SERVICE_TYPE_BONJOUR = 1; // 0x1
+ field public static final int SERVICE_TYPE_UPNP = 2; // 0x2
+ field public static final int SERVICE_TYPE_VENDOR_SPECIFIC = 255; // 0xff
+ }
+
+ public class WifiP2pServiceRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public static android.net.wifi.p2p.nsd.WifiP2pServiceRequest newInstance(int, String);
+ method public static android.net.wifi.p2p.nsd.WifiP2pServiceRequest newInstance(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
+ public class WifiP2pUpnpServiceInfo extends android.net.wifi.p2p.nsd.WifiP2pServiceInfo {
+ method public static android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo newInstance(String, String, java.util.List<java.lang.String>);
+ }
+
+ public class WifiP2pUpnpServiceRequest extends android.net.wifi.p2p.nsd.WifiP2pServiceRequest {
+ method public static android.net.wifi.p2p.nsd.WifiP2pUpnpServiceRequest newInstance();
+ method public static android.net.wifi.p2p.nsd.WifiP2pUpnpServiceRequest newInstance(String);
+ }
+
+}
+
+package android.net.wifi.rtt {
+
+ public class CivicLocationKeys {
+ field public static final int ADDITIONAL_CODE = 32; // 0x20
+ field public static final int APT = 26; // 0x1a
+ field public static final int BOROUGH = 4; // 0x4
+ field public static final int BRANCH_ROAD_NAME = 36; // 0x24
+ field public static final int BUILDING = 25; // 0x19
+ field public static final int CITY = 3; // 0x3
+ field public static final int COUNTY = 2; // 0x2
+ field public static final int DESK = 33; // 0x21
+ field public static final int FLOOR = 27; // 0x1b
+ field public static final int GROUP_OF_STREETS = 6; // 0x6
+ field public static final int HNO = 19; // 0x13
+ field public static final int HNS = 20; // 0x14
+ field public static final int LANGUAGE = 0; // 0x0
+ field public static final int LMK = 21; // 0x15
+ field public static final int LOC = 22; // 0x16
+ field public static final int NAM = 23; // 0x17
+ field public static final int NEIGHBORHOOD = 5; // 0x5
+ field public static final int PCN = 30; // 0x1e
+ field public static final int POD = 17; // 0x11
+ field public static final int POSTAL_CODE = 24; // 0x18
+ field public static final int PO_BOX = 31; // 0x1f
+ field public static final int PRD = 16; // 0x10
+ field public static final int PRIMARY_ROAD_NAME = 34; // 0x22
+ field public static final int ROAD_SECTION = 35; // 0x23
+ field public static final int ROOM = 28; // 0x1c
+ field public static final int SCRIPT = 128; // 0x80
+ field public static final int STATE = 1; // 0x1
+ field public static final int STREET_NAME_POST_MODIFIER = 39; // 0x27
+ field public static final int STREET_NAME_PRE_MODIFIER = 38; // 0x26
+ field public static final int STS = 18; // 0x12
+ field public static final int SUBBRANCH_ROAD_NAME = 37; // 0x25
+ field public static final int TYPE_OF_PLACE = 29; // 0x1d
+ }
+
+ public final class RangingRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public static int getMaxPeers();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingRequest> CREATOR;
+ }
+
+ public static final class RangingRequest.Builder {
+ ctor public RangingRequest.Builder();
+ method public android.net.wifi.rtt.RangingRequest.Builder addAccessPoint(@NonNull android.net.wifi.ScanResult);
+ method public android.net.wifi.rtt.RangingRequest.Builder addAccessPoints(@NonNull java.util.List<android.net.wifi.ScanResult>);
+ method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(@NonNull android.net.MacAddress);
+ method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(@NonNull android.net.wifi.aware.PeerHandle);
+ method public android.net.wifi.rtt.RangingRequest build();
+ }
+
+ public final class RangingResult implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getDistanceMm();
+ method public int getDistanceStdDevMm();
+ method @Nullable public android.net.MacAddress getMacAddress();
+ method public int getNumAttemptedMeasurements();
+ method public int getNumSuccessfulMeasurements();
+ method @Nullable public android.net.wifi.aware.PeerHandle getPeerHandle();
+ method public long getRangingTimestampMillis();
+ method public int getRssi();
+ method public int getStatus();
+ method @Nullable public android.net.wifi.rtt.ResponderLocation getUnverifiedResponderLocation();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingResult> CREATOR;
+ field public static final int STATUS_FAIL = 1; // 0x1
+ field public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2; // 0x2
+ field public static final int STATUS_SUCCESS = 0; // 0x0
+ }
+
+ public abstract class RangingResultCallback {
+ ctor public RangingResultCallback();
+ method public abstract void onRangingFailure(int);
+ method public abstract void onRangingResults(@NonNull java.util.List<android.net.wifi.rtt.RangingResult>);
+ field public static final int STATUS_CODE_FAIL = 1; // 0x1
+ field public static final int STATUS_CODE_FAIL_RTT_NOT_AVAILABLE = 2; // 0x2
+ }
+
+ public final class ResponderLocation implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getAltitude();
+ method public int getAltitudeType();
+ method public double getAltitudeUncertainty();
+ method public java.util.List<android.net.MacAddress> getColocatedBssids();
+ method public int getDatum();
+ method public int getExpectedToMove();
+ method public double getFloorNumber();
+ method public double getHeightAboveFloorMeters();
+ method public double getHeightAboveFloorUncertaintyMeters();
+ method public double getLatitude();
+ method public double getLatitudeUncertainty();
+ method public int getLciVersion();
+ method public double getLongitude();
+ method public double getLongitudeUncertainty();
+ method @Nullable public String getMapImageMimeType();
+ method @Nullable public android.net.Uri getMapImageUri();
+ method public boolean getRegisteredLocationAgreementIndication();
+ method public boolean isLciSubelementValid();
+ method public boolean isZaxisSubelementValid();
+ method @Nullable public android.location.Address toCivicLocationAddress();
+ method @Nullable public android.util.SparseArray<java.lang.String> toCivicLocationSparseArray();
+ method @NonNull public android.location.Location toLocation();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ALTITUDE_FLOORS = 2; // 0x2
+ field public static final int ALTITUDE_METERS = 1; // 0x1
+ field public static final int ALTITUDE_UNDEFINED = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.ResponderLocation> CREATOR;
+ field public static final int DATUM_NAD83_MLLW = 3; // 0x3
+ field public static final int DATUM_NAD83_NAV88 = 2; // 0x2
+ field public static final int DATUM_UNDEFINED = 0; // 0x0
+ field public static final int DATUM_WGS84 = 1; // 0x1
+ field public static final int LCI_VERSION_1 = 1; // 0x1
+ field public static final int LOCATION_FIXED = 0; // 0x0
+ field public static final int LOCATION_MOVEMENT_UNKNOWN = 2; // 0x2
+ field public static final int LOCATION_RESERVED = 3; // 0x3
+ field public static final int LOCATION_VARIABLE = 1; // 0x1
+ }
+
+ public class WifiRttManager {
+ method public boolean isAvailable();
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_WIFI_STATE}) public void startRanging(@NonNull android.net.wifi.rtt.RangingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.rtt.RangingResultCallback);
+ field public static final String ACTION_WIFI_RTT_STATE_CHANGED = "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED";
+ }
+
+}
+
diff --git a/wifi/api/module-lib-current.txt b/wifi/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/wifi/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/wifi/api/module-lib-removed.txt b/wifi/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/wifi/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/wifi/api/removed.txt b/wifi/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/wifi/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
new file mode 100644
index 0000000..150a650
--- /dev/null
+++ b/wifi/api/system-current.txt
@@ -0,0 +1,939 @@
+// Signature format: 2.0
+package android.net.wifi {
+
+ public abstract class EasyConnectStatusCallback {
+ ctor public EasyConnectStatusCallback();
+ method public abstract void onConfiguratorSuccess(int);
+ method public abstract void onEnrolleeSuccess(int);
+ method public void onFailure(int);
+ method public void onFailure(int, @Nullable String, @NonNull android.util.SparseArray<int[]>, @NonNull int[]);
+ method public abstract void onProgress(int);
+ field public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
+ field public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3; // 0x3
+ field public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2; // 0x2
+ field public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1
+ field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1; // 0x1
+ field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
+ }
+
+ @Deprecated public class RttManager {
+ method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
+ method @Deprecated public android.net.wifi.RttManager.Capabilities getCapabilities();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.net.wifi.RttManager.RttCapabilities getRttCapabilities();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startRanging(android.net.wifi.RttManager.RttParams[], android.net.wifi.RttManager.RttListener);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopRanging(android.net.wifi.RttManager.RttListener);
+ field @Deprecated public static final int BASE;
+ field @Deprecated public static final int CMD_OP_ABORTED;
+ field @Deprecated public static final int CMD_OP_DISABLE_RESPONDER;
+ field @Deprecated public static final int CMD_OP_ENABLE_RESPONDER;
+ field @Deprecated public static final int CMD_OP_ENALBE_RESPONDER_FAILED;
+ field @Deprecated public static final int CMD_OP_ENALBE_RESPONDER_SUCCEEDED;
+ field @Deprecated public static final int CMD_OP_FAILED;
+ field @Deprecated public static final int CMD_OP_START_RANGING;
+ field @Deprecated public static final int CMD_OP_STOP_RANGING;
+ field @Deprecated public static final int CMD_OP_SUCCEEDED;
+ field @Deprecated public static final String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description";
+ field @Deprecated public static final int PREAMBLE_HT = 2; // 0x2
+ field @Deprecated public static final int PREAMBLE_LEGACY = 1; // 0x1
+ field @Deprecated public static final int PREAMBLE_VHT = 4; // 0x4
+ field @Deprecated public static final int REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON = -6; // 0xfffffffa
+ field @Deprecated public static final int REASON_INVALID_LISTENER = -3; // 0xfffffffd
+ field @Deprecated public static final int REASON_INVALID_REQUEST = -4; // 0xfffffffc
+ field @Deprecated public static final int REASON_NOT_AVAILABLE = -2; // 0xfffffffe
+ field @Deprecated public static final int REASON_PERMISSION_DENIED = -5; // 0xfffffffb
+ field @Deprecated public static final int REASON_UNSPECIFIED = -1; // 0xffffffff
+ field @Deprecated public static final int RTT_BW_10_SUPPORT = 2; // 0x2
+ field @Deprecated public static final int RTT_BW_160_SUPPORT = 32; // 0x20
+ field @Deprecated public static final int RTT_BW_20_SUPPORT = 4; // 0x4
+ field @Deprecated public static final int RTT_BW_40_SUPPORT = 8; // 0x8
+ field @Deprecated public static final int RTT_BW_5_SUPPORT = 1; // 0x1
+ field @Deprecated public static final int RTT_BW_80_SUPPORT = 16; // 0x10
+ field @Deprecated public static final int RTT_CHANNEL_WIDTH_10 = 6; // 0x6
+ field @Deprecated public static final int RTT_CHANNEL_WIDTH_160 = 3; // 0x3
+ field @Deprecated public static final int RTT_CHANNEL_WIDTH_20 = 0; // 0x0
+ field @Deprecated public static final int RTT_CHANNEL_WIDTH_40 = 1; // 0x1
+ field @Deprecated public static final int RTT_CHANNEL_WIDTH_5 = 5; // 0x5
+ field @Deprecated public static final int RTT_CHANNEL_WIDTH_80 = 2; // 0x2
+ field @Deprecated public static final int RTT_CHANNEL_WIDTH_80P80 = 4; // 0x4
+ field @Deprecated public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1; // 0xffffffff
+ field @Deprecated public static final int RTT_PEER_NAN = 5; // 0x5
+ field @Deprecated public static final int RTT_PEER_P2P_CLIENT = 4; // 0x4
+ field @Deprecated public static final int RTT_PEER_P2P_GO = 3; // 0x3
+ field @Deprecated public static final int RTT_PEER_TYPE_AP = 1; // 0x1
+ field @Deprecated public static final int RTT_PEER_TYPE_STA = 2; // 0x2
+ field @Deprecated public static final int RTT_PEER_TYPE_UNSPECIFIED = 0; // 0x0
+ field @Deprecated public static final int RTT_STATUS_ABORTED = 8; // 0x8
+ field @Deprecated public static final int RTT_STATUS_FAILURE = 1; // 0x1
+ field @Deprecated public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6; // 0x6
+ field @Deprecated public static final int RTT_STATUS_FAIL_BUSY_TRY_LATER = 12; // 0xc
+ field @Deprecated public static final int RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE = 15; // 0xf
+ field @Deprecated public static final int RTT_STATUS_FAIL_INVALID_TS = 9; // 0x9
+ field @Deprecated public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET = 4; // 0x4
+ field @Deprecated public static final int RTT_STATUS_FAIL_NO_CAPABILITY = 7; // 0x7
+ field @Deprecated public static final int RTT_STATUS_FAIL_NO_RSP = 2; // 0x2
+ field @Deprecated public static final int RTT_STATUS_FAIL_PROTOCOL = 10; // 0xa
+ field @Deprecated public static final int RTT_STATUS_FAIL_REJECTED = 3; // 0x3
+ field @Deprecated public static final int RTT_STATUS_FAIL_SCHEDULE = 11; // 0xb
+ field @Deprecated public static final int RTT_STATUS_FAIL_TM_TIMEOUT = 5; // 0x5
+ field @Deprecated public static final int RTT_STATUS_INVALID_REQ = 13; // 0xd
+ field @Deprecated public static final int RTT_STATUS_NO_WIFI = 14; // 0xe
+ field @Deprecated public static final int RTT_STATUS_SUCCESS = 0; // 0x0
+ field @Deprecated public static final int RTT_TYPE_11_MC = 4; // 0x4
+ field @Deprecated public static final int RTT_TYPE_11_V = 2; // 0x2
+ field @Deprecated public static final int RTT_TYPE_ONE_SIDED = 1; // 0x1
+ field @Deprecated public static final int RTT_TYPE_TWO_SIDED = 2; // 0x2
+ field @Deprecated public static final int RTT_TYPE_UNSPECIFIED = 0; // 0x0
+ }
+
+ @Deprecated public class RttManager.Capabilities {
+ ctor @Deprecated public RttManager.Capabilities();
+ field @Deprecated public int supportedPeerType;
+ field @Deprecated public int supportedType;
+ }
+
+ @Deprecated public static class RttManager.ParcelableRttParams implements android.os.Parcelable {
+ field @Deprecated @NonNull public android.net.wifi.RttManager.RttParams[] mParams;
+ }
+
+ @Deprecated public static class RttManager.ParcelableRttResults implements android.os.Parcelable {
+ ctor @Deprecated public RttManager.ParcelableRttResults(android.net.wifi.RttManager.RttResult[]);
+ field @Deprecated public android.net.wifi.RttManager.RttResult[] mResults;
+ }
+
+ @Deprecated public abstract static class RttManager.ResponderCallback {
+ ctor @Deprecated public RttManager.ResponderCallback();
+ method @Deprecated public abstract void onResponderEnableFailure(int);
+ method @Deprecated public abstract void onResponderEnabled(android.net.wifi.RttManager.ResponderConfig);
+ }
+
+ @Deprecated public static class RttManager.ResponderConfig implements android.os.Parcelable {
+ ctor @Deprecated public RttManager.ResponderConfig();
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.RttManager.ResponderConfig> CREATOR;
+ field @Deprecated public int centerFreq0;
+ field @Deprecated public int centerFreq1;
+ field @Deprecated public int channelWidth;
+ field @Deprecated public int frequency;
+ field @Deprecated public String macAddress;
+ field @Deprecated public int preamble;
+ }
+
+ @Deprecated public static class RttManager.RttCapabilities implements android.os.Parcelable {
+ ctor @Deprecated public RttManager.RttCapabilities();
+ field @Deprecated public int bwSupported;
+ field @Deprecated public boolean lciSupported;
+ field @Deprecated public boolean lcrSupported;
+ field @Deprecated public int mcVersion;
+ field @Deprecated public boolean oneSidedRttSupported;
+ field @Deprecated public int preambleSupported;
+ field @Deprecated public boolean responderSupported;
+ field @Deprecated public boolean secureRttSupported;
+ field @Deprecated public boolean supportedPeerType;
+ field @Deprecated public boolean supportedType;
+ field @Deprecated public boolean twoSided11McRttSupported;
+ }
+
+ @Deprecated public static interface RttManager.RttListener {
+ method @Deprecated public void onAborted();
+ method @Deprecated public void onFailure(int, String);
+ method @Deprecated public void onSuccess(android.net.wifi.RttManager.RttResult[]);
+ }
+
+ @Deprecated public static class RttManager.RttParams {
+ ctor @Deprecated public RttManager.RttParams();
+ field @Deprecated public boolean LCIRequest;
+ field @Deprecated public boolean LCRRequest;
+ field @Deprecated public int bandwidth;
+ field @Deprecated public String bssid;
+ field @Deprecated public int burstTimeout;
+ field @Deprecated public int centerFreq0;
+ field @Deprecated public int centerFreq1;
+ field @Deprecated public int channelWidth;
+ field @Deprecated public int deviceType;
+ field @Deprecated public int frequency;
+ field @Deprecated public int interval;
+ field @Deprecated public int numRetriesPerFTMR;
+ field @Deprecated public int numRetriesPerMeasurementFrame;
+ field @Deprecated public int numSamplesPerBurst;
+ field @Deprecated public int num_retries;
+ field @Deprecated public int num_samples;
+ field @Deprecated public int numberBurst;
+ field @Deprecated public int preamble;
+ field @Deprecated public int requestType;
+ field @Deprecated public boolean secure;
+ }
+
+ @Deprecated public static class RttManager.RttResult {
+ ctor @Deprecated public RttManager.RttResult();
+ field @Deprecated public android.net.wifi.RttManager.WifiInformationElement LCI;
+ field @Deprecated public android.net.wifi.RttManager.WifiInformationElement LCR;
+ field @Deprecated public String bssid;
+ field @Deprecated public int burstDuration;
+ field @Deprecated public int burstNumber;
+ field @Deprecated public int distance;
+ field @Deprecated public int distanceSpread;
+ field @Deprecated public int distanceStandardDeviation;
+ field @Deprecated public int distance_cm;
+ field @Deprecated public int distance_sd_cm;
+ field @Deprecated public int distance_spread_cm;
+ field @Deprecated public int frameNumberPerBurstPeer;
+ field @Deprecated public int measurementFrameNumber;
+ field @Deprecated public int measurementType;
+ field @Deprecated public int negotiatedBurstNum;
+ field @Deprecated public int requestType;
+ field @Deprecated public int retryAfterDuration;
+ field @Deprecated public int rssi;
+ field @Deprecated public int rssiSpread;
+ field @Deprecated public int rssi_spread;
+ field @Deprecated public long rtt;
+ field @Deprecated public long rttSpread;
+ field @Deprecated public long rttStandardDeviation;
+ field @Deprecated public long rtt_ns;
+ field @Deprecated public long rtt_sd_ns;
+ field @Deprecated public long rtt_spread_ns;
+ field @Deprecated public int rxRate;
+ field @Deprecated public boolean secure;
+ field @Deprecated public int status;
+ field @Deprecated public int successMeasurementFrameNumber;
+ field @Deprecated public long ts;
+ field @Deprecated public int txRate;
+ field @Deprecated public int tx_rate;
+ }
+
+ @Deprecated public static class RttManager.WifiInformationElement {
+ ctor @Deprecated public RttManager.WifiInformationElement();
+ field @Deprecated public byte[] data;
+ field @Deprecated public byte id;
+ }
+
+ public class ScanResult implements android.os.Parcelable {
+ field public static final int CIPHER_CCMP = 3; // 0x3
+ field public static final int CIPHER_GCMP_256 = 4; // 0x4
+ field public static final int CIPHER_NONE = 0; // 0x0
+ field public static final int CIPHER_NO_GROUP_ADDRESSED = 1; // 0x1
+ field public static final int CIPHER_SMS4 = 5; // 0x5
+ field public static final int CIPHER_TKIP = 2; // 0x2
+ field public static final int KEY_MGMT_EAP = 2; // 0x2
+ field public static final int KEY_MGMT_EAP_SHA256 = 6; // 0x6
+ field public static final int KEY_MGMT_EAP_SUITE_B_192 = 10; // 0xa
+ field public static final int KEY_MGMT_FT_EAP = 4; // 0x4
+ field public static final int KEY_MGMT_FT_PSK = 3; // 0x3
+ field public static final int KEY_MGMT_FT_SAE = 11; // 0xb
+ field public static final int KEY_MGMT_NONE = 0; // 0x0
+ field public static final int KEY_MGMT_OSEN = 7; // 0x7
+ field public static final int KEY_MGMT_OWE = 9; // 0x9
+ field public static final int KEY_MGMT_OWE_TRANSITION = 12; // 0xc
+ field public static final int KEY_MGMT_PSK = 1; // 0x1
+ field public static final int KEY_MGMT_PSK_SHA256 = 5; // 0x5
+ field public static final int KEY_MGMT_SAE = 8; // 0x8
+ field public static final int KEY_MGMT_WAPI_CERT = 14; // 0xe
+ field public static final int KEY_MGMT_WAPI_PSK = 13; // 0xd
+ field public static final int PROTOCOL_NONE = 0; // 0x0
+ field public static final int PROTOCOL_OSEN = 3; // 0x3
+ field public static final int PROTOCOL_RSN = 2; // 0x2
+ field public static final int PROTOCOL_WAPI = 4; // 0x4
+ field public static final int PROTOCOL_WPA = 1; // 0x1
+ }
+
+ public final class SoftApCapability implements android.os.Parcelable {
+ method public boolean areFeaturesSupported(long);
+ method public int describeContents();
+ method public int getMaxSupportedClients();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
+ field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L
+ field public static final long SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2L; // 0x2L
+ field public static final long SOFTAP_FEATURE_WPA3_SAE = 4L; // 0x4L
+ }
+
+ public final class SoftApConfiguration implements android.os.Parcelable {
+ method @NonNull public java.util.List<android.net.MacAddress> getAllowedClientList();
+ method public int getBand();
+ method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList();
+ method public int getChannel();
+ method public int getMaxNumberOfClients();
+ method public long getShutdownTimeoutMillis();
+ method public boolean isAutoShutdownEnabled();
+ method public boolean isClientControlByUserEnabled();
+ method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration();
+ field public static final int BAND_2GHZ = 1; // 0x1
+ field public static final int BAND_5GHZ = 2; // 0x2
+ field public static final int BAND_6GHZ = 4; // 0x4
+ field public static final int BAND_ANY = 7; // 0x7
+ }
+
+ public static final class SoftApConfiguration.Builder {
+ ctor public SoftApConfiguration.Builder();
+ ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
+ method @NonNull public android.net.wifi.SoftApConfiguration build();
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAllowedClientList(@NonNull java.util.List<android.net.MacAddress>);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBlockedClientList(@NonNull java.util.List<android.net.MacAddress>);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientControlByUserEnabled(boolean);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) long);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
+ }
+
+ public final class SoftApInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getBandwidth();
+ method public int getFrequency();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CHANNEL_WIDTH_160MHZ = 6; // 0x6
+ field public static final int CHANNEL_WIDTH_20MHZ = 2; // 0x2
+ field public static final int CHANNEL_WIDTH_20MHZ_NOHT = 1; // 0x1
+ field public static final int CHANNEL_WIDTH_40MHZ = 3; // 0x3
+ field public static final int CHANNEL_WIDTH_80MHZ = 4; // 0x4
+ field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 5; // 0x5
+ field public static final int CHANNEL_WIDTH_INVALID = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApInfo> CREATOR;
+ }
+
+ public final class WifiClient implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiClient> CREATOR;
+ }
+
+ @Deprecated public class WifiConfiguration implements android.os.Parcelable {
+ method @Deprecated public int getAuthType();
+ method @Deprecated @NonNull public android.net.IpConfiguration getIpConfiguration();
+ method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus getNetworkSelectionStatus();
+ method @Deprecated @NonNull public String getPrintableSsid();
+ method @Deprecated public int getRecentFailureReason();
+ method @Deprecated public boolean hasNoInternetAccess();
+ method @Deprecated public boolean isEphemeral();
+ method @Deprecated public static boolean isMetered(@Nullable android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiInfo);
+ method @Deprecated public boolean isNoInternetAccessExpected();
+ method @Deprecated public void setIpConfiguration(@Nullable android.net.IpConfiguration);
+ method @Deprecated public void setNetworkSelectionStatus(@NonNull android.net.wifi.WifiConfiguration.NetworkSelectionStatus);
+ field @Deprecated public static final int INVALID_NETWORK_ID = -1; // 0xffffffff
+ field @Deprecated public static final int METERED_OVERRIDE_METERED = 1; // 0x1
+ field @Deprecated public static final int METERED_OVERRIDE_NONE = 0; // 0x0
+ field @Deprecated public static final int METERED_OVERRIDE_NOT_METERED = 2; // 0x2
+ field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
+ field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
+ field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
+ field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
+ field @Deprecated public boolean allowAutojoin;
+ field @Deprecated public int carrierId;
+ field @Deprecated public String creatorName;
+ field @Deprecated public int creatorUid;
+ field @Deprecated public boolean fromWifiNetworkSpecifier;
+ field @Deprecated public boolean fromWifiNetworkSuggestion;
+ field @Deprecated public String lastUpdateName;
+ field @Deprecated public int lastUpdateUid;
+ field @Deprecated public int macRandomizationSetting;
+ field @Deprecated public boolean meteredHint;
+ field @Deprecated public int meteredOverride;
+ field @Deprecated public int numAssociation;
+ field @Deprecated public int numScorerOverride;
+ field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
+ field @Deprecated public boolean requirePmf;
+ field @Deprecated public boolean shared;
+ field @Deprecated public boolean useExternalScores;
+ }
+
+ @Deprecated public static class WifiConfiguration.KeyMgmt {
+ field @Deprecated public static final int WAPI_CERT = 14; // 0xe
+ field @Deprecated public static final int WAPI_PSK = 13; // 0xd
+ field @Deprecated public static final int WPA2_PSK = 4; // 0x4
+ }
+
+ @Deprecated public static class WifiConfiguration.NetworkSelectionStatus {
+ method @Deprecated public int getDisableReasonCounter(int);
+ method @Deprecated public long getDisableTime();
+ method @Deprecated public static int getMaxNetworkSelectionDisableReason();
+ method @Deprecated public int getNetworkSelectionDisableReason();
+ method @Deprecated @Nullable public static String getNetworkSelectionDisableReasonString(int);
+ method @Deprecated public int getNetworkSelectionStatus();
+ method @Deprecated @NonNull public String getNetworkStatusString();
+ method @Deprecated public boolean hasEverConnected();
+ field @Deprecated public static final int DISABLED_ASSOCIATION_REJECTION = 1; // 0x1
+ field @Deprecated public static final int DISABLED_AUTHENTICATION_FAILURE = 2; // 0x2
+ field @Deprecated public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 5; // 0x5
+ field @Deprecated public static final int DISABLED_AUTHENTICATION_NO_SUBSCRIPTION = 9; // 0x9
+ field @Deprecated public static final int DISABLED_BY_WIFI_MANAGER = 7; // 0x7
+ field @Deprecated public static final int DISABLED_BY_WRONG_PASSWORD = 8; // 0x8
+ field @Deprecated public static final int DISABLED_DHCP_FAILURE = 3; // 0x3
+ field @Deprecated public static final int DISABLED_NONE = 0; // 0x0
+ field @Deprecated public static final int DISABLED_NO_INTERNET_PERMANENT = 6; // 0x6
+ field @Deprecated public static final int DISABLED_NO_INTERNET_TEMPORARY = 4; // 0x4
+ field @Deprecated public static final int NETWORK_SELECTION_ENABLED = 0; // 0x0
+ field @Deprecated public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2; // 0x2
+ field @Deprecated public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1; // 0x1
+ }
+
+ @Deprecated public static final class WifiConfiguration.NetworkSelectionStatus.Builder {
+ ctor @Deprecated public WifiConfiguration.NetworkSelectionStatus.Builder();
+ method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus build();
+ method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus.Builder setNetworkSelectionDisableReason(int);
+ method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus.Builder setNetworkSelectionStatus(int);
+ }
+
+ public class WifiEnterpriseConfig implements android.os.Parcelable {
+ method @Nullable public String[] getCaCertificateAliases();
+ method @NonNull public String getCaPath();
+ method @NonNull public String getClientCertificateAlias();
+ method public int getOcsp();
+ method @NonNull public String getWapiCertSuite();
+ method public void setCaCertificateAliases(@Nullable String[]);
+ method public void setCaPath(@NonNull String);
+ method public void setClientCertificateAlias(@NonNull String);
+ method public void setOcsp(int);
+ method public void setWapiCertSuite(@NonNull String);
+ field public static final int OCSP_NONE = 0; // 0x0
+ field public static final int OCSP_REQUEST_CERT_STATUS = 1; // 0x1
+ field public static final int OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS = 3; // 0x3
+ field public static final int OCSP_REQUIRE_CERT_STATUS = 2; // 0x2
+ }
+
+ public class WifiFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ }
+
+ public class WifiInfo implements android.os.Parcelable {
+ method public double getLostTxPacketsPerSecond();
+ method @Nullable public String getRequestingPackageName();
+ method public double getRetriedTxPacketsPerSecond();
+ method public int getScore();
+ method public double getSuccessfulRxPacketsPerSecond();
+ method public double getSuccessfulTxPacketsPerSecond();
+ method public boolean isEphemeral();
+ method public boolean isOsuAp();
+ method public boolean isPasspointAp();
+ method @Nullable public static String sanitizeSsid(@Nullable String);
+ field public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
+ field public static final int INVALID_RSSI = -127; // 0xffffff81
+ }
+
+ public class WifiManager {
+ method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinGlobal(boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean);
+ method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void clearWifiConnectedNetworkScorer();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK}) public void disableEphemeralNetwork(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
+ method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork();
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>);
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>);
+ method public boolean isApMacRandomizationSupported();
+ method public boolean isConnectedMacRandomizationSupported();
+ method @Deprecated public boolean isDeviceToDeviceRttSupported();
+ method public boolean isPortableHotspotSupported();
+ method public boolean isVerboseLoggingEnabled();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
+ method public boolean isWifiScannerSupported();
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
+ method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]);
+ method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration restoreSoftApBackupData(@NonNull byte[]);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSupplicantBackupData(@NonNull byte[], @NonNull byte[]);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData();
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setPasspointMeteredOverride(@NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
+ method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startTetheredHotspot(@Nullable android.net.wifi.SoftApConfiguration);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int);
+ method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void updateWifiUsabilityScore(int, int, int);
+ field public static final String ACTION_LINK_CONFIGURATION_CHANGED = "android.net.wifi.LINK_CONFIGURATION_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public static final String ACTION_NETWORK_SETTINGS_RESET = "android.net.wifi.action.NETWORK_SETTINGS_RESET";
+ field public static final String ACTION_PASSPOINT_LAUNCH_OSU_VIEW = "android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW";
+ field public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE";
+ field public static final String ACTION_REQUEST_ENABLE = "android.net.wifi.action.REQUEST_ENABLE";
+ field public static final int CHANGE_REASON_ADDED = 0; // 0x0
+ field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
+ field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
+ field public static final String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
+ field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1
+ field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
+ field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
+ field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
+ field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1
+ field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
+ field public static final String EXTRA_CHANGE_REASON = "changeReason";
+ field public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
+ field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
+ field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
+ field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
+ field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
+ field public static final String EXTRA_WIFI_AP_FAILURE_REASON = "android.net.wifi.extra.WIFI_AP_FAILURE_REASON";
+ field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
+ field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
+ field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
+ field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
+ field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
+ field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
+ field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0
+ field public static final int IFACE_IP_MODE_LOCAL_ONLY = 2; // 0x2
+ field public static final int IFACE_IP_MODE_TETHERED = 1; // 0x1
+ field public static final int IFACE_IP_MODE_UNSPECIFIED = -1; // 0xffffffff
+ field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0
+ field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1
+ field public static final int SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0; // 0x0
+ field public static final int SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1; // 0x1
+ field public static final int SAP_START_FAILURE_GENERAL = 0; // 0x0
+ field public static final int SAP_START_FAILURE_NO_CHANNEL = 1; // 0x1
+ field public static final int SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION = 2; // 0x2
+ field public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED";
+ field public static final int WIFI_AP_STATE_DISABLED = 11; // 0xb
+ field public static final int WIFI_AP_STATE_DISABLING = 10; // 0xa
+ field public static final int WIFI_AP_STATE_ENABLED = 13; // 0xd
+ field public static final int WIFI_AP_STATE_ENABLING = 12; // 0xc
+ field public static final int WIFI_AP_STATE_FAILED = 14; // 0xe
+ field public static final String WIFI_CREDENTIAL_CHANGED_ACTION = "android.net.wifi.WIFI_CREDENTIAL_CHANGED";
+ field public static final int WIFI_CREDENTIAL_FORGOT = 1; // 0x1
+ field public static final int WIFI_CREDENTIAL_SAVED = 0; // 0x0
+ }
+
+ public static interface WifiManager.ActionListener {
+ method public void onFailure(int);
+ method public void onSuccess();
+ }
+
+ public static interface WifiManager.NetworkRequestMatchCallback {
+ method public default void onAbort();
+ method public default void onMatch(@NonNull java.util.List<android.net.wifi.ScanResult>);
+ method public default void onUserSelectionCallbackRegistration(@NonNull android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback);
+ method public default void onUserSelectionConnectFailure(@NonNull android.net.wifi.WifiConfiguration);
+ method public default void onUserSelectionConnectSuccess(@NonNull android.net.wifi.WifiConfiguration);
+ }
+
+ public static interface WifiManager.NetworkRequestUserSelectionCallback {
+ method public default void reject();
+ method public default void select(@NonNull android.net.wifi.WifiConfiguration);
+ }
+
+ public static interface WifiManager.OnWifiActivityEnergyInfoListener {
+ method public void onWifiActivityEnergyInfo(@Nullable android.os.connectivity.WifiActivityEnergyInfo);
+ }
+
+ public static interface WifiManager.OnWifiUsabilityStatsListener {
+ method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry);
+ }
+
+ public static interface WifiManager.ScoreUpdateObserver {
+ method public void notifyScoreUpdate(int, int);
+ method public void triggerUpdateOfWifiUsabilityStats(int);
+ }
+
+ public static interface WifiManager.SoftApCallback {
+ method public default void onBlockedClientConnecting(@NonNull android.net.wifi.WifiClient, int);
+ method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability);
+ method public default void onConnectedClientsChanged(@NonNull java.util.List<android.net.wifi.WifiClient>);
+ method public default void onInfoChanged(@NonNull android.net.wifi.SoftApInfo);
+ method public default void onStateChanged(int, int);
+ }
+
+ public static interface WifiManager.TrafficStateCallback {
+ method public void onStateChanged(int);
+ field public static final int DATA_ACTIVITY_IN = 1; // 0x1
+ field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
+ field public static final int DATA_ACTIVITY_NONE = 0; // 0x0
+ field public static final int DATA_ACTIVITY_OUT = 2; // 0x2
+ }
+
+ public static interface WifiManager.WifiConnectedNetworkScorer {
+ method public void onSetScoreUpdateObserver(@NonNull android.net.wifi.WifiManager.ScoreUpdateObserver);
+ method public void onStart(int);
+ method public void onStop(int);
+ }
+
+ public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
+ ctor public WifiNetworkConnectionStatistics(int, int);
+ ctor public WifiNetworkConnectionStatistics();
+ ctor public WifiNetworkConnectionStatistics(android.net.wifi.WifiNetworkConnectionStatistics);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkConnectionStatistics> CREATOR;
+ field public int numConnection;
+ field public int numUsage;
+ }
+
+ public final class WifiNetworkSuggestion implements android.os.Parcelable {
+ method @NonNull public android.net.wifi.WifiConfiguration getWifiConfiguration();
+ }
+
+ public static final class WifiNetworkSuggestion.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
+ }
+
+ public class WifiScanner {
+ method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
+ method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
+ method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults();
+ method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<android.net.wifi.ScanResult> getSingleScanResults();
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void registerScanListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiScanner.ScanListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setScanningEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource);
+ method @Deprecated public void startTrackingBssids(android.net.wifi.WifiScanner.BssidInfo[], int, android.net.wifi.WifiScanner.BssidListener);
+ method @Deprecated public void startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopBackgroundScan(android.net.wifi.WifiScanner.ScanListener);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopScan(android.net.wifi.WifiScanner.ScanListener);
+ method @Deprecated public void stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener);
+ method @Deprecated public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
+ method public void unregisterScanListener(@NonNull android.net.wifi.WifiScanner.ScanListener);
+ field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000
+ field public static final int MIN_SCAN_PERIOD_MS = 1000; // 0x3e8
+ field public static final int REASON_DUPLICATE_REQEUST = -5; // 0xfffffffb
+ field public static final int REASON_INVALID_LISTENER = -2; // 0xfffffffe
+ field public static final int REASON_INVALID_REQUEST = -3; // 0xfffffffd
+ field public static final int REASON_NOT_AUTHORIZED = -4; // 0xfffffffc
+ field public static final int REASON_SUCCEEDED = 0; // 0x0
+ field public static final int REASON_UNSPECIFIED = -1; // 0xffffffff
+ field @Deprecated public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0; // 0x0
+ field public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; // 0x1
+ field public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; // 0x2
+ field public static final int REPORT_EVENT_NO_BATCH = 4; // 0x4
+ field public static final int SCAN_TYPE_HIGH_ACCURACY = 2; // 0x2
+ field public static final int SCAN_TYPE_LOW_LATENCY = 0; // 0x0
+ field public static final int SCAN_TYPE_LOW_POWER = 1; // 0x1
+ field public static final int WIFI_BAND_24_5_6_GHZ = 11; // 0xb
+ field public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ = 15; // 0xf
+ field public static final int WIFI_BAND_24_GHZ = 1; // 0x1
+ field public static final int WIFI_BAND_5_GHZ = 2; // 0x2
+ field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4
+ field public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; // 0x6
+ field public static final int WIFI_BAND_6_GHZ = 8; // 0x8
+ field public static final int WIFI_BAND_BOTH = 3; // 0x3
+ field public static final int WIFI_BAND_BOTH_WITH_DFS = 7; // 0x7
+ field public static final int WIFI_BAND_UNSPECIFIED = 0; // 0x0
+ }
+
+ public static interface WifiScanner.ActionListener {
+ method public void onFailure(int, String);
+ method public void onSuccess();
+ }
+
+ @Deprecated public static class WifiScanner.BssidInfo {
+ ctor @Deprecated public WifiScanner.BssidInfo();
+ field @Deprecated public String bssid;
+ field @Deprecated public int frequencyHint;
+ field @Deprecated public int high;
+ field @Deprecated public int low;
+ }
+
+ @Deprecated public static interface WifiScanner.BssidListener extends android.net.wifi.WifiScanner.ActionListener {
+ method @Deprecated public void onFound(android.net.wifi.ScanResult[]);
+ method @Deprecated public void onLost(android.net.wifi.ScanResult[]);
+ }
+
+ public static class WifiScanner.ChannelSpec {
+ ctor public WifiScanner.ChannelSpec(int);
+ field public int frequency;
+ }
+
+ @Deprecated public static class WifiScanner.HotlistSettings implements android.os.Parcelable {
+ ctor @Deprecated public WifiScanner.HotlistSettings();
+ field @Deprecated public int apLostThreshold;
+ field @Deprecated public android.net.wifi.WifiScanner.BssidInfo[] bssidInfos;
+ }
+
+ public static class WifiScanner.ParcelableScanData implements android.os.Parcelable {
+ ctor public WifiScanner.ParcelableScanData(android.net.wifi.WifiScanner.ScanData[]);
+ method public android.net.wifi.WifiScanner.ScanData[] getResults();
+ field public android.net.wifi.WifiScanner.ScanData[] mResults;
+ }
+
+ public static class WifiScanner.ParcelableScanResults implements android.os.Parcelable {
+ ctor public WifiScanner.ParcelableScanResults(android.net.wifi.ScanResult[]);
+ method public android.net.wifi.ScanResult[] getResults();
+ field public android.net.wifi.ScanResult[] mResults;
+ }
+
+ public static class WifiScanner.ScanData implements android.os.Parcelable {
+ ctor public WifiScanner.ScanData(int, int, android.net.wifi.ScanResult[]);
+ ctor public WifiScanner.ScanData(android.net.wifi.WifiScanner.ScanData);
+ method public int getFlags();
+ method public int getId();
+ method public android.net.wifi.ScanResult[] getResults();
+ }
+
+ public static interface WifiScanner.ScanListener extends android.net.wifi.WifiScanner.ActionListener {
+ method public void onFullResult(android.net.wifi.ScanResult);
+ method @Deprecated public void onPeriodChanged(int);
+ method public void onResults(android.net.wifi.WifiScanner.ScanData[]);
+ }
+
+ public static class WifiScanner.ScanSettings implements android.os.Parcelable {
+ ctor public WifiScanner.ScanSettings();
+ field public int band;
+ field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
+ field @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public final java.util.List<android.net.wifi.WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks;
+ field public boolean hideFromAppOps;
+ field public boolean ignoreLocationSettings;
+ field @Deprecated public int maxPeriodInMs;
+ field @Deprecated public int maxScansToCache;
+ field @Deprecated public int numBssidsPerScan;
+ field @Deprecated public int periodInMs;
+ field @Deprecated public int reportEvents;
+ field @Deprecated public int stepCount;
+ field @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public int type;
+ }
+
+ public static class WifiScanner.ScanSettings.HiddenNetwork {
+ ctor public WifiScanner.ScanSettings.HiddenNetwork(@NonNull String);
+ field @NonNull public final String ssid;
+ }
+
+ @Deprecated public static interface WifiScanner.WifiChangeListener extends android.net.wifi.WifiScanner.ActionListener {
+ method @Deprecated public void onChanging(android.net.wifi.ScanResult[]);
+ method @Deprecated public void onQuiescence(android.net.wifi.ScanResult[]);
+ }
+
+ @Deprecated public static class WifiScanner.WifiChangeSettings implements android.os.Parcelable {
+ ctor @Deprecated public WifiScanner.WifiChangeSettings();
+ field @Deprecated public android.net.wifi.WifiScanner.BssidInfo[] bssidInfos;
+ field @Deprecated public int lostApSampleSize;
+ field @Deprecated public int minApsBreachingThreshold;
+ field @Deprecated public int periodInMs;
+ field @Deprecated public int rssiSampleSize;
+ field @Deprecated public int unchangedSampleSize;
+ }
+
+ public final class WifiUsabilityStatsEntry implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCellularDataNetworkType();
+ method public int getCellularSignalStrengthDb();
+ method public int getCellularSignalStrengthDbm();
+ method public int getLinkSpeedMbps();
+ method public int getProbeElapsedTimeSinceLastUpdateMillis();
+ method public int getProbeMcsRateSinceLastUpdate();
+ method public int getProbeStatusSinceLastUpdate();
+ method public int getRssi();
+ method public int getRxLinkSpeedMbps();
+ method public long getTimeStampMillis();
+ method public long getTotalBackgroundScanTimeMillis();
+ method public long getTotalBeaconRx();
+ method public long getTotalCcaBusyFreqTimeMillis();
+ method public long getTotalHotspot2ScanTimeMillis();
+ method public long getTotalNanScanTimeMillis();
+ method public long getTotalPnoScanTimeMillis();
+ method public long getTotalRadioOnFreqTimeMillis();
+ method public long getTotalRadioOnTimeMillis();
+ method public long getTotalRadioRxTimeMillis();
+ method public long getTotalRadioTxTimeMillis();
+ method public long getTotalRoamScanTimeMillis();
+ method public long getTotalRxSuccess();
+ method public long getTotalScanTimeMillis();
+ method public long getTotalTxBad();
+ method public long getTotalTxRetries();
+ method public long getTotalTxSuccess();
+ method public boolean isSameRegisteredCell();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiUsabilityStatsEntry> CREATOR;
+ field public static final int PROBE_STATUS_FAILURE = 3; // 0x3
+ field public static final int PROBE_STATUS_NO_PROBE = 1; // 0x1
+ field public static final int PROBE_STATUS_SUCCESS = 2; // 0x2
+ field public static final int PROBE_STATUS_UNKNOWN = 0; // 0x0
+ }
+
+}
+
+package android.net.wifi.aware {
+
+ public class DiscoverySession implements java.lang.AutoCloseable {
+ method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]);
+ }
+
+ public class WifiAwareSession implements java.lang.AutoCloseable {
+ method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]);
+ }
+
+}
+
+package android.net.wifi.hotspot2 {
+
+ public final class OsuProvider implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getFriendlyName();
+ method @Nullable public android.net.Uri getServerUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.OsuProvider> CREATOR;
+ }
+
+ public final class PasspointConfiguration implements android.os.Parcelable {
+ method public int getMeteredOverride();
+ method public boolean isAutojoinEnabled();
+ method public boolean isMacRandomizationEnabled();
+ }
+
+ public abstract class ProvisioningCallback {
+ ctor public ProvisioningCallback();
+ method public abstract void onProvisioningComplete();
+ method public abstract void onProvisioningFailure(int);
+ method public abstract void onProvisioningStatus(int);
+ field public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22; // 0x16
+ field public static final int OSU_FAILURE_AP_CONNECTION = 1; // 0x1
+ field public static final int OSU_FAILURE_INVALID_URL_FORMAT_FOR_OSU = 8; // 0x8
+ field public static final int OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE = 17; // 0x11
+ field public static final int OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE = 21; // 0x15
+ field public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 14; // 0xe
+ field public static final int OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE = 19; // 0x13
+ field public static final int OSU_FAILURE_NO_PPS_MO = 16; // 0x10
+ field public static final int OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE = 18; // 0x12
+ field public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23; // 0x17
+ field public static final int OSU_FAILURE_PROVISIONING_ABORTED = 6; // 0x6
+ field public static final int OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7; // 0x7
+ field public static final int OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES = 20; // 0x14
+ field public static final int OSU_FAILURE_SERVER_CONNECTION = 3; // 0x3
+ field public static final int OSU_FAILURE_SERVER_URL_INVALID = 2; // 0x2
+ field public static final int OSU_FAILURE_SERVER_VALIDATION = 4; // 0x4
+ field public static final int OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION = 5; // 0x5
+ field public static final int OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11; // 0xb
+ field public static final int OSU_FAILURE_START_REDIRECT_LISTENER = 12; // 0xc
+ field public static final int OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER = 13; // 0xd
+ field public static final int OSU_FAILURE_UNEXPECTED_COMMAND_TYPE = 9; // 0x9
+ field public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS = 15; // 0xf
+ field public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE = 10; // 0xa
+ field public static final int OSU_STATUS_AP_CONNECTED = 2; // 0x2
+ field public static final int OSU_STATUS_AP_CONNECTING = 1; // 0x1
+ field public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6; // 0x6
+ field public static final int OSU_STATUS_REDIRECT_RESPONSE_RECEIVED = 8; // 0x8
+ field public static final int OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS = 11; // 0xb
+ field public static final int OSU_STATUS_SECOND_SOAP_EXCHANGE = 9; // 0x9
+ field public static final int OSU_STATUS_SERVER_CONNECTED = 5; // 0x5
+ field public static final int OSU_STATUS_SERVER_CONNECTING = 3; // 0x3
+ field public static final int OSU_STATUS_SERVER_VALIDATED = 4; // 0x4
+ field public static final int OSU_STATUS_THIRD_SOAP_EXCHANGE = 10; // 0xa
+ field public static final int OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE = 7; // 0x7
+ }
+
+}
+
+package android.net.wifi.p2p {
+
+ public final class WifiP2pGroupList implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.wifi.p2p.WifiP2pGroup> getGroupList();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroupList> CREATOR;
+ }
+
+ public class WifiP2pManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void deletePersistentGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public void requestPersistentGroupInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setDeviceName(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull String, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setMiracastMode(int);
+ method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pWfdInfo, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setWifiP2pChannels(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void startListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void stopListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+ field public static final String ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED";
+ field public static final int MIRACAST_DISABLED = 0; // 0x0
+ field public static final int MIRACAST_SINK = 2; // 0x2
+ field public static final int MIRACAST_SOURCE = 1; // 0x1
+ }
+
+ public static interface WifiP2pManager.PersistentGroupInfoListener {
+ method public void onPersistentGroupInfoAvailable(@NonNull android.net.wifi.p2p.WifiP2pGroupList);
+ }
+
+}
+
+package android.net.wifi.rtt {
+
+ public static final class RangingRequest.Builder {
+ method public android.net.wifi.rtt.RangingRequest.Builder addResponder(@NonNull android.net.wifi.rtt.ResponderConfig);
+ }
+
+ public final class RangingResult implements android.os.Parcelable {
+ method @NonNull public byte[] getLci();
+ method @NonNull public byte[] getLcr();
+ }
+
+ public final class ResponderConfig implements android.os.Parcelable {
+ ctor public ResponderConfig(@NonNull android.net.MacAddress, int, boolean, int, int, int, int, int);
+ ctor public ResponderConfig(@NonNull android.net.wifi.aware.PeerHandle, int, boolean, int, int, int, int, int);
+ method public int describeContents();
+ method public static android.net.wifi.rtt.ResponderConfig fromScanResult(android.net.wifi.ScanResult);
+ method public static android.net.wifi.rtt.ResponderConfig fromWifiAwarePeerHandleWithDefaults(android.net.wifi.aware.PeerHandle);
+ method public static android.net.wifi.rtt.ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(android.net.MacAddress);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3
+ field public static final int CHANNEL_WIDTH_20MHZ = 0; // 0x0
+ field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1
+ field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2
+ field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.ResponderConfig> CREATOR;
+ field public static final int PREAMBLE_HE = 3; // 0x3
+ field public static final int PREAMBLE_HT = 1; // 0x1
+ field public static final int PREAMBLE_LEGACY = 0; // 0x0
+ field public static final int PREAMBLE_VHT = 2; // 0x2
+ field public static final int RESPONDER_AP = 0; // 0x0
+ field public static final int RESPONDER_AWARE = 4; // 0x4
+ field public static final int RESPONDER_P2P_CLIENT = 3; // 0x3
+ field public static final int RESPONDER_P2P_GO = 2; // 0x2
+ field public static final int RESPONDER_STA = 1; // 0x1
+ field public final int centerFreq0;
+ field public final int centerFreq1;
+ field public final int channelWidth;
+ field public final int frequency;
+ field public final android.net.MacAddress macAddress;
+ field public final android.net.wifi.aware.PeerHandle peerHandle;
+ field public final int preamble;
+ field public final int responderType;
+ field public final boolean supports80211mc;
+ }
+
+ public final class ResponderLocation implements android.os.Parcelable {
+ method public boolean getExtraInfoOnAssociationIndication();
+ }
+
+ public class WifiRttManager {
+ method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE}) public void cancelRanging(@Nullable android.os.WorkSource);
+ method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_WIFI_STATE}) public void startRanging(@Nullable android.os.WorkSource, @NonNull android.net.wifi.rtt.RangingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.rtt.RangingResultCallback);
+ }
+
+}
+
diff --git a/wifi/api/system-removed.txt b/wifi/api/system-removed.txt
new file mode 100644
index 0000000..35f9726
--- /dev/null
+++ b/wifi/api/system-removed.txt
@@ -0,0 +1,16 @@
+// Signature format: 2.0
+package android.net.wifi {
+
+ @Deprecated public class BatchedScanResult implements android.os.Parcelable {
+ ctor public BatchedScanResult();
+ ctor public BatchedScanResult(android.net.wifi.BatchedScanResult);
+ field public final java.util.List<android.net.wifi.ScanResult> scanResults;
+ field public boolean truncated;
+ }
+
+ public class ScanResult implements android.os.Parcelable {
+ field public boolean untrusted;
+ }
+
+}
+
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index 950361c..eeb006e 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -1,7 +1,32 @@
+# used by wifi-service
+rule android.net.DhcpResultsParcelable* @0
+rule android.net.DhcpResults* com.android.server.x.wifi.net.DhcpResults@1
rule android.net.InterfaceConfigurationParcel* @0
rule android.net.InterfaceConfiguration* com.android.server.x.wifi.net.InterfaceConfiguration@1
+rule android.net.IpMemoryStore* com.android.server.x.wifi.net.IpMemoryStore@1
+rule android.net.NetworkMonitorManager* com.android.server.x.wifi.net.NetworkMonitorManager@1
+rule android.net.TcpKeepalivePacketData* com.android.server.x.wifi.net.TcpKeepalivePacketData@1
rule android.net.NetworkFactory* com.android.server.x.wifi.net.NetworkFactory@1
+rule android.net.ip.IpClientCallbacks* com.android.server.x.wifi.net.ip.IpClientCallbacks@1
+rule android.net.ip.IpClientManager* com.android.server.x.wifi.net.ip.IpClientManager@1
+rule android.net.ip.IpClientUtil* com.android.server.x.wifi.net.ip.IpClientUtil@1
+rule android.net.shared.InetAddressUtils* com.android.server.x.wifi.net.shared.InetAddressUtils@1
+rule android.net.shared.InitialConfiguration* com.android.server.x.wifi.net.shared.InitialConfiguration@1
+rule android.net.shared.IpConfigurationParcelableUtil* com.android.server.x.wifi.net.shared.IpConfigurationParcelableUtil@1
+rule android.net.shared.LinkPropertiesParcelableUtil* com.android.server.x.wifi.net.shared.LinkPropertiesParcelableUtil@1
+rule android.net.shared.ParcelableUtil* com.android.server.x.wifi.net.shared.ParcelableUtil@1
+rule android.net.shared.NetdUtils* com.android.server.x.wifi.net.shared.NetdUtils@1
+rule android.net.shared.NetworkMonitorUtils* com.android.server.x.wifi.net.shared.NetworkMonitorUtils@1
+rule android.net.shared.ParcelableUtil* com.android.server.x.wifi.net.shared.ParcelableUtil@1
+rule android.net.shared.PrivateDnsConfig* com.android.server.x.wifi.net.shared.PrivateDnsConfig@1
+rule android.net.shared.ProvisioningConfiguration* com.android.server.x.wifi.net.shared.ProvisioningConfiguration@1
+rule android.net.shared.RouteUtils* com.android.server.x.wifi.net.shared.RouteUtils@1
+rule android.net.util.KeepalivePacketDataUtil* com.android.server.x.wifi.net.util.KeepalivePacketDataUtil@1
+rule android.net.util.NetworkConstants* com.android.server.x.wifi.net.util.NetworkConstants@1
+rule android.net.util.InterfaceParams* com.android.server.x.wifi.net.util.InterfaceParams@1
+rule android.net.util.SharedLog* com.android.server.x.wifi.net.util.SharedLog@1
rule android.net.util.NetUtils* com.android.server.x.wifi.net.util.NetUtils@1
+rule android.net.util.IpUtils* com.android.server.x.wifi.net.util.IpUtils@1
# We don't jar-jar the entire package because, we still use some classes (like
# AsyncChannel in com.android.internal.util) from these packages which are not
@@ -29,7 +54,6 @@
# Use our statically linked PlatformProperties library
rule android.sysprop.** com.android.server.x.wifi.sysprop.@1
-
# used by both framework-wifi and wifi-service
rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1
rule android.content.pm.ParceledListSlice* android.x.net.wifi.util.ParceledListSlice@1
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 70542b5..727952c 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -695,31 +695,6 @@
*/
public AnqpInformationElement[] anqpElements;
- /**
- * Flag indicating if this AP is a carrier AP. The determination is based
- * on the AP's SSID and if AP is using EAP security.
- *
- * @hide
- */
- // TODO(b/144431927): remove once migrated to Suggestions
- public boolean isCarrierAp;
-
- /**
- * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
- *
- * @hide
- */
- // TODO(b/144431927): remove once migrated to Suggestions
- public int carrierApEapType;
-
- /**
- * The name of the carrier that's associated with this AP if it is a carrier AP.
- *
- * @hide
- */
- // TODO(b/144431927): remove once migrated to Suggestions
- public String carrierName;
-
/** {@hide} */
public ScanResult(WifiSsid wifiSsid, String BSSID, long hessid, int anqpDomainId,
byte[] osuProviders, String caps, int level, int frequency, long tsf) {
@@ -744,9 +719,6 @@
this.centerFreq0 = UNSPECIFIED;
this.centerFreq1 = UNSPECIFIED;
this.flags = 0;
- this.isCarrierAp = false;
- this.carrierApEapType = UNSPECIFIED;
- this.carrierName = null;
this.radioChainInfos = null;
this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
}
@@ -767,9 +739,6 @@
this.centerFreq0 = UNSPECIFIED;
this.centerFreq1 = UNSPECIFIED;
this.flags = 0;
- this.isCarrierAp = false;
- this.carrierApEapType = UNSPECIFIED;
- this.carrierName = null;
this.radioChainInfos = null;
this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
}
@@ -797,9 +766,6 @@
} else {
this.flags = 0;
}
- this.isCarrierAp = false;
- this.carrierApEapType = UNSPECIFIED;
- this.carrierName = null;
this.radioChainInfos = null;
this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
}
@@ -839,9 +805,6 @@
venueName = source.venueName;
operatorFriendlyName = source.operatorFriendlyName;
flags = source.flags;
- isCarrierAp = source.isCarrierAp;
- carrierApEapType = source.carrierApEapType;
- carrierName = source.carrierName;
radioChainInfos = source.radioChainInfos;
this.mWifiStandard = source.mWifiStandard;
}
@@ -881,9 +844,6 @@
sb.append(", standard: ").append(wifiStandardToString(mWifiStandard));
sb.append(", 80211mcResponder: ");
sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported");
- sb.append(", Carrier AP: ").append(isCarrierAp ? "yes" : "no");
- sb.append(", Carrier AP EAP Type: ").append(carrierApEapType);
- sb.append(", Carrier name: ").append(carrierName);
sb.append(", Radio Chain Infos: ").append(Arrays.toString(radioChainInfos));
return sb.toString();
}
@@ -954,9 +914,6 @@
} else {
dest.writeInt(0);
}
- dest.writeInt(isCarrierAp ? 1 : 0);
- dest.writeInt(carrierApEapType);
- dest.writeString(carrierName);
if (radioChainInfos != null) {
dest.writeInt(radioChainInfos.length);
@@ -1036,9 +993,6 @@
new AnqpInformationElement(vendorId, elementId, payload);
}
}
- sr.isCarrierAp = in.readInt() != 0;
- sr.carrierApEapType = in.readInt();
- sr.carrierName = in.readString();
n = in.readInt();
if (n != 0) {
sr.radioChainInfos = new RadioChainInfo[n];
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 2b47623..a269e17 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -35,7 +35,6 @@
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -211,7 +210,7 @@
* Delay in milliseconds before shutting down soft AP when
* there are no connected devices.
*/
- private final int mShutdownTimeoutMillis;
+ private final long mShutdownTimeoutMillis;
/**
* THe definition of security type OPEN.
@@ -247,7 +246,7 @@
private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
@Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
@SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled,
- int shutdownTimeoutMillis, boolean clientControlByUser,
+ long shutdownTimeoutMillis, boolean clientControlByUser,
@NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) {
mSsid = ssid;
mBssid = bssid;
@@ -327,7 +326,7 @@
dest.writeInt(mSecurityType);
dest.writeInt(mMaxNumberOfClients);
dest.writeBoolean(mAutoShutdownEnabled);
- dest.writeInt(mShutdownTimeoutMillis);
+ dest.writeLong(mShutdownTimeoutMillis);
dest.writeBoolean(mClientControlByUser);
dest.writeTypedList(mBlockedClientList);
dest.writeTypedList(mAllowedClientList);
@@ -346,7 +345,7 @@
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
- in.readInt(), in.readBoolean(), in.readInt(), in.readBoolean(),
+ in.readInt(), in.readBoolean(), in.readLong(), in.readBoolean(),
in.createTypedArrayList(MacAddress.CREATOR),
in.createTypedArrayList(MacAddress.CREATOR));
}
@@ -454,19 +453,19 @@
/**
* Returns the shutdown timeout in milliseconds.
* The Soft AP will shutdown when there are no devices associated to it for
- * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}.
+ * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(long)}.
*
* @hide
*/
@SystemApi
- public int getShutdownTimeoutMillis() {
+ public long getShutdownTimeoutMillis() {
return mShutdownTimeoutMillis;
}
/**
* Returns a flag indicating whether clients need to be pre-approved by the user.
* (true: authorization required) or not (false: not required).
- * {@link Builder#enableClientControlByUser(Boolean)}.
+ * {@link Builder#setClientControlByUserEnabled(Boolean)}.
*
* @hide
*/
@@ -478,7 +477,7 @@
/**
* Returns List of clients which aren't allowed to associate to the AP.
*
- * Clients are configured using {@link Builder#setClientList(List, List)}
+ * Clients are configured using {@link Builder#setBlockedClientList(List)}
*
* @hide
*/
@@ -490,7 +489,7 @@
/**
* List of clients which are allowed to associate to the AP.
- * Clients are configured using {@link Builder#setClientList(List, List)}
+ * Clients are configured using {@link Builder#setAllowedClientList(List)}
*
* @hide
*/
@@ -575,7 +574,7 @@
private int mMaxNumberOfClients;
private int mSecurityType;
private boolean mAutoShutdownEnabled;
- private int mShutdownTimeoutMillis;
+ private long mShutdownTimeoutMillis;
private boolean mClientControlByUser;
private List<MacAddress> mBlockedClientList;
private List<MacAddress> mAllowedClientList;
@@ -627,6 +626,11 @@
*/
@NonNull
public SoftApConfiguration build() {
+ for (MacAddress client : mAllowedClientList) {
+ if (mBlockedClientList.contains(client)) {
+ throw new IllegalArgumentException("A MacAddress exist in both client list");
+ }
+ }
return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
@@ -835,7 +839,7 @@
* @param enable true to enable, false to disable.
* @return Builder for chaining.
*
- * @see #setShutdownTimeoutMillis(int)
+ * @see #setShutdownTimeoutMillis(long)
*/
@NonNull
public Builder setAutoShutdownEnabled(boolean enable) {
@@ -862,7 +866,7 @@
* @see #setAutoShutdownEnabled(boolean)
*/
@NonNull
- public Builder setShutdownTimeoutMillis(@IntRange(from = 0) int timeoutMillis) {
+ public Builder setShutdownTimeoutMillis(@IntRange(from = 0) long timeoutMillis) {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("Invalid timeout value");
}
@@ -878,7 +882,7 @@
*
* If manual user control is enabled then clients will be accepted, rejected, or require
* a user approval based on the configuration provided by
- * {@link #setClientList(List, List)}.
+ * {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}.
*
* <p>
* This method requires hardware support. Hardware support can be determined using
@@ -898,26 +902,48 @@
* @return Builder for chaining.
*/
@NonNull
- public Builder enableClientControlByUser(boolean enabled) {
+ public Builder setClientControlByUserEnabled(boolean enabled) {
mClientControlByUser = enabled;
return this;
}
/**
- * This method together with {@link enableClientControlByUser(boolean)} control client
- * connections to the AP. If {@link enableClientControlByUser(false)} is configured than
+ * This method together with {@link setClientControlByUserEnabled(boolean)} control client
+ * connections to the AP. If client control by user is disabled using the above method then
* this API has no effect and clients are allowed to associate to the AP (within limit of
* max number of clients).
*
- * If {@link enableClientControlByUser(true)} is configured then this API configures
- * 2 lists:
+ * If client control by user is enabled then this API configures the list of clients
+ * which are explicitly allowed. These are auto-accepted.
+ *
+ * All other clients which attempt to associate, whose MAC addresses are on neither list,
+ * are:
* <ul>
- * <li>List of clients which are blocked. These are rejected.</li>
- * <li>List of clients which are explicitly allowed. These are auto-accepted.</li>
+ * <li>Rejected</li>
+ * <li>A callback {@link WifiManager.SoftApCallback#onBlockedClientConnecting(WifiClient)}
+ * is issued (which allows the user to add them to the allowed client list if desired).<li>
* </ul>
*
- * <p>
+ * @param allowedClientList list of clients which are allowed to associate to the AP
+ * without user pre-approval.
+ * @return Builder for chaining.
+ */
+ @NonNull
+ public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) {
+ mAllowedClientList = new ArrayList<>(allowedClientList);
+ return this;
+ }
+
+ /**
+ * This method together with {@link setClientControlByUserEnabled(boolean)} control client
+ * connections to the AP. If client control by user is disabled using the above method then
+ * this API has no effect and clients are allowed to associate to the AP (within limit of
+ * max number of clients).
+ *
+ * If client control by user is enabled then this API this API configures the list of
+ * clients which are blocked. These are rejected.
+ *
* All other clients which attempt to associate, whose MAC addresses are on neither list,
* are:
* <ul>
@@ -927,23 +953,11 @@
* </ul>
*
* @param blockedClientList list of clients which are not allowed to associate to the AP.
- * @param allowedClientList list of clients which are allowed to associate to the AP
- * without user pre-approval.
* @return Builder for chaining.
*/
@NonNull
- public Builder setClientList(@NonNull List<MacAddress> blockedClientList,
- @NonNull List<MacAddress> allowedClientList) {
+ public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) {
mBlockedClientList = new ArrayList<>(blockedClientList);
- mAllowedClientList = new ArrayList<>(allowedClientList);
- Iterator<MacAddress> iterator = mAllowedClientList.iterator();
- while (iterator.hasNext()) {
- MacAddress client = iterator.next();
- int index = mBlockedClientList.indexOf(client);
- if (index != -1) {
- throw new IllegalArgumentException("A MacAddress exist in both list");
- }
- }
return this;
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 96beacd..e1acaf8 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -727,8 +727,9 @@
/**
* If Soft Ap client is blocked, this reason code means that client doesn't exist in the
- * specified configuration {@link SoftApConfiguration.Builder#setClientList(List, List)}
- * and the {@link SoftApConfiguration.Builder#enableClientControlByUser(true)}
+ * specified configuration {@link SoftApConfiguration.Builder#setBlockedClientList(List)}
+ * and {@link SoftApConfiguration.Builder#setAllowedClientList(List)}
+ * and the {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)}
* is configured as well.
* @hide
*/
@@ -1352,7 +1353,7 @@
try {
ParceledListSlice<WifiConfiguration> parceledList =
mService.getConfiguredNetworks(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1369,7 +1370,7 @@
try {
ParceledListSlice<WifiConfiguration> parceledList =
mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1898,7 +1899,7 @@
@NonNull List<WifiNetworkSuggestion> networkSuggestions) {
try {
return mService.addNetworkSuggestions(
- networkSuggestions, mContext.getOpPackageName(), mContext.getFeatureId());
+ networkSuggestions, mContext.getOpPackageName(), mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2680,8 +2681,8 @@
public boolean startScan(WorkSource workSource) {
try {
String packageName = mContext.getOpPackageName();
- String featureId = mContext.getFeatureId();
- return mService.startScan(packageName, featureId);
+ String attributionTag = mContext.getAttributionTag();
+ return mService.startScan(packageName, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2713,7 +2714,7 @@
public WifiInfo getConnectionInfo() {
try {
return mService.getConnectionInfo(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2728,7 +2729,7 @@
public List<ScanResult> getScanResults() {
try {
return mService.getScanResults(mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2759,7 +2760,7 @@
try {
return mService.getMatchingScanResults(
networkSuggestionsToMatch, scanResults,
- mContext.getOpPackageName(), mContext.getFeatureId());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3198,7 +3199,7 @@
new LocalOnlyHotspotCallbackProxy(this, executor, callback);
try {
String packageName = mContext.getOpPackageName();
- String featureId = mContext.getFeatureId();
+ String featureId = mContext.getAttributionTag();
int returnCode = mService.startLocalOnlyHotspot(proxy, packageName, featureId,
config);
if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
@@ -3409,9 +3410,10 @@
* If the API is called while the tethered soft AP is enabled, the configuration will apply to
* the current soft AP if the new configuration only includes
* {@link SoftApConfiguration.Builder#setMaxNumberOfClients(int)}
- * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(int)}
- * or {@link SoftApConfiguration.Builder#enableClientControlByUser(boolean)}
- * or {@link SoftApConfiguration.Builder#setClientList(List, List)}.
+ * or {@link SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)}
+ * or {@link SoftApConfiguration.Builder#setClientControlByUserEnabled(boolean)}
+ * or {@link SoftApConfiguration.Builder#setBlockedClientList(List)}
+ * or {@link SoftApConfiguration.Builder#setAllowedClientList(List)}
*
* Otherwise, the configuration changes will be applied when the Soft AP is next started
* (the framework will not stop/start the AP).
@@ -5889,7 +5891,7 @@
try {
mService.registerSuggestionConnectionStatusListener(new Binder(),
new SuggestionConnectionStatusListenerProxy(executor, listener),
- listener.hashCode(), mContext.getOpPackageName(), mContext.getFeatureId());
+ listener.hashCode(), mContext.getOpPackageName(), mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/java/android/net/wifi/WifiMigration.java
index a3482d7..f2a1aec 100755
--- a/wifi/java/android/net/wifi/WifiMigration.java
+++ b/wifi/java/android/net/wifi/WifiMigration.java
@@ -16,186 +16,249 @@
package android.net.wifi;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static android.os.Environment.getDataMiscCeDirectory;
+import static android.os.Environment.getDataMiscDirectory;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.provider.Settings;
+import android.util.AtomicFile;
+import android.util.SparseArray;
-import java.util.List;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Class used to provide one time hooks for existing OEM devices to migrate their config store
- * data and other settings to the wifi mainline module.
+ * data and other settings to the wifi apex.
* @hide
*/
@SystemApi
public final class WifiMigration {
+ /**
+ * Directory to read the wifi config store files from under.
+ */
+ private static final String LEGACY_WIFI_STORE_DIRECTORY_NAME = "wifi";
+ /**
+ * Config store file for general shared store file.
+ * AOSP Path on Android 10: /data/misc/wifi/WifiConfigStore.xml
+ */
+ public static final int STORE_FILE_SHARED_GENERAL = 0;
+ /**
+ * Config store file for softap shared store file.
+ * AOSP Path on Android 10: /data/misc/wifi/softap.conf
+ */
+ public static final int STORE_FILE_SHARED_SOFTAP = 1;
+ /**
+ * Config store file for general user store file.
+ * AOSP Path on Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStore.xml
+ */
+ public static final int STORE_FILE_USER_GENERAL = 2;
+ /**
+ * Config store file for network suggestions user store file.
+ * AOSP Path on Android 10: /data/misc_ce/<userId>/wifi/WifiConfigStoreNetworkSuggestions.xml
+ */
+ public static final int STORE_FILE_USER_NETWORK_SUGGESTIONS = 3;
+
+ /** @hide */
+ @IntDef(prefix = { "STORE_FILE_SHARED_" }, value = {
+ STORE_FILE_SHARED_GENERAL,
+ STORE_FILE_SHARED_SOFTAP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SharedStoreFileId { }
+
+ /** @hide */
+ @IntDef(prefix = { "STORE_FILE_USER_" }, value = {
+ STORE_FILE_USER_GENERAL,
+ STORE_FILE_USER_NETWORK_SUGGESTIONS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserStoreFileId { }
+
+ /**
+ * Mapping of Store file Id to Store file names.
+ *
+ * NOTE: This is the default path for the files on AOSP devices. If the OEM has modified
+ * the path or renamed the files, please edit this appropriately.
+ */
+ private static final SparseArray<String> STORE_ID_TO_FILE_NAME =
+ new SparseArray<String>() {{
+ put(STORE_FILE_SHARED_GENERAL, "WifiConfigStore.xml");
+ put(STORE_FILE_SHARED_SOFTAP, "softap.conf");
+ put(STORE_FILE_USER_GENERAL, "WifiConfigStore.xml");
+ put(STORE_FILE_USER_NETWORK_SUGGESTIONS, "WifiConfigStoreNetworkSuggestions.xml");
+ }};
+
+ /**
+ * Pre-apex wifi shared folder.
+ */
+ private static File getLegacyWifiSharedDirectory() {
+ return new File(getDataMiscDirectory(), LEGACY_WIFI_STORE_DIRECTORY_NAME);
+ }
+
+ /**
+ * Pre-apex wifi user folder.
+ */
+ private static File getLegacyWifiUserDirectory(int userId) {
+ return new File(getDataMiscCeDirectory(userId), LEGACY_WIFI_STORE_DIRECTORY_NAME);
+ }
+
+ /**
+ * Legacy files were stored as AtomicFile. So, always use AtomicFile to operate on it to ensure
+ * data integrity.
+ */
+ private static AtomicFile getSharedAtomicFile(@SharedStoreFileId int storeFileId) {
+ return new AtomicFile(new File(
+ getLegacyWifiSharedDirectory(),
+ STORE_ID_TO_FILE_NAME.get(storeFileId)));
+ }
+
+ /**
+ * Legacy files were stored as AtomicFile. So, always use AtomicFile to operate on it to ensure
+ * data integrity.
+ */
+ private static AtomicFile getUserAtomicFile(@UserStoreFileId int storeFileId, int userId) {
+ return new AtomicFile(new File(
+ getLegacyWifiUserDirectory(userId),
+ STORE_ID_TO_FILE_NAME.get(storeFileId)));
+ }
private WifiMigration() { }
/**
- * Container for all the wifi config data to migrate.
- */
- public static final class ConfigStoreMigrationData implements Parcelable {
- /**
- * Builder to create instance of {@link ConfigStoreMigrationData}.
- */
- public static final class Builder {
- private List<WifiConfiguration> mUserSavedNetworkConfigurations;
- private SoftApConfiguration mUserSoftApConfiguration;
-
- public Builder() {
- mUserSavedNetworkConfigurations = null;
- mUserSoftApConfiguration = null;
- }
-
- /**
- * Sets the list of all user's saved network configurations parsed from OEM config
- * store files.
- *
- * @param userSavedNetworkConfigurations List of {@link WifiConfiguration} representing
- * the list of user's saved networks
- * @return Instance of {@link Builder} to enable chaining of the builder method.
- */
- public @NonNull Builder setUserSavedNetworkConfigurations(
- @NonNull List<WifiConfiguration> userSavedNetworkConfigurations) {
- checkNotNull(userSavedNetworkConfigurations);
- mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
- return this;
- }
-
- /**
- * Sets the user's softap configuration parsed from OEM config store files.
- *
- * @param userSoftApConfiguration {@link SoftApConfiguration} representing user's
- * SoftAp configuration
- * @return Instance of {@link Builder} to enable chaining of the builder method.
- */
- public @NonNull Builder setUserSoftApConfiguration(
- @NonNull SoftApConfiguration userSoftApConfiguration) {
- checkNotNull(userSoftApConfiguration);
- mUserSoftApConfiguration = userSoftApConfiguration;
- return this;
- }
-
- /**
- * Build an instance of {@link ConfigStoreMigrationData}.
- *
- * @return Instance of {@link ConfigStoreMigrationData}.
- */
- public @NonNull ConfigStoreMigrationData build() {
- return new ConfigStoreMigrationData(
- mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
- }
- }
-
- private final List<WifiConfiguration> mUserSavedNetworkConfigurations;
- private final SoftApConfiguration mUserSoftApConfiguration;
-
- private ConfigStoreMigrationData(
- @Nullable List<WifiConfiguration> userSavedNetworkConfigurations,
- @Nullable SoftApConfiguration userSoftApConfiguration) {
- mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
- mUserSoftApConfiguration = userSoftApConfiguration;
- }
-
- public static final @NonNull Parcelable.Creator<ConfigStoreMigrationData> CREATOR =
- new Parcelable.Creator<ConfigStoreMigrationData>() {
- @Override
- public ConfigStoreMigrationData createFromParcel(Parcel in) {
- List<WifiConfiguration> userSavedNetworkConfigurations =
- in.readArrayList(null);
- SoftApConfiguration userSoftApConfiguration = in.readParcelable(null);
- return new ConfigStoreMigrationData(
- userSavedNetworkConfigurations, userSoftApConfiguration);
- }
-
- @Override
- public ConfigStoreMigrationData[] newArray(int size) {
- return new ConfigStoreMigrationData[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeList(mUserSavedNetworkConfigurations);
- dest.writeParcelable(mUserSoftApConfiguration, flags);
- }
-
- /**
- * Returns list of all user's saved network configurations.
- *
- * Note: Only to be returned if there is any format change in how OEM persisted this info.
- * @return List of {@link WifiConfiguration} representing the list of user's saved networks,
- * or null if no migration necessary.
- */
- @Nullable
- public List<WifiConfiguration> getUserSavedNetworkConfigurations() {
- return mUserSavedNetworkConfigurations;
- }
-
- /**
- * Returns user's softap configuration.
- *
- * Note: Only to be returned if there is any format change in how OEM persisted this info.
- * @return {@link SoftApConfiguration} representing user's SoftAp configuration,
- * or null if no migration necessary.
- */
- @Nullable
- public SoftApConfiguration getUserSoftApConfiguration() {
- return mUserSoftApConfiguration;
- }
- }
-
- /**
- * Load data from OEM's config store.
+ * Load data from legacy shared wifi config store file.
+ * TODO(b/149418926): Add XSD for the AOSP file format for each file from R.
* <p>
* Note:
- * <li>OEMs need to implement {@link #loadFromConfigStore()} ()} only if their
- * existing config store format or file locations differs from the vanilla AOSP implementation.
- * </li>
- * <li>The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every
- * bootup, its the responsibility of the OEM implementation to ensure that this method returns
- * non-null data only on the first bootup. Once the migration is done, the OEM can safely delete
- * their config store files when {@link #removeConfigStore()} is invoked.
- * <li>The first & only relevant invocation of {@link #loadFromConfigStore()} occurs when a
- * previously released device upgrades to the wifi mainline module from an OEM implementation
- * of the wifi stack.
+ * <li>OEMs need to change the implementation of
+ * {@link #convertAndRetrieveSharedConfigStoreFile(int)} only if their existing config store
+ * format or file locations differs from the vanilla AOSP implementation.</li>
+ * <li>The wifi apex will invoke
+ * {@link #convertAndRetrieveSharedConfigStoreFile(int)}
+ * method on every bootup, it is the responsibility of the OEM implementation to ensure that
+ * they perform the necessary in place conversion of their config store file to conform to the
+ * AOSP format. The OEM should ensure that the method should only return the
+ * {@link InputStream} stream for the data to be migrated only on the first bootup.</li>
+ * <li>Once the migration is done, the apex will invoke
+ * {@link #removeSharedConfigStoreFile(int)} to delete the store file.</li>
+ * <li>The only relevant invocation of {@link #convertAndRetrieveSharedConfigStoreFile(int)}
+ * occurs when a previously released device upgrades to the wifi apex from an OEM
+ * implementation of the wifi stack.
+ * <li>Ensure that the legacy file paths are accessible to the wifi module (sepolicy rules, file
+ * permissions, etc). Since the wifi service continues to run inside system_server process, this
+ * method will be called from the same context (so ideally the file should still be accessible).
* </li>
*
- * @return Instance of {@link ConfigStoreMigrationData} for migrating data, null if no
- * migration is necessary.
+ * @param storeFileId Identifier for the config store file. One of
+ * {@link #STORE_FILE_SHARED_GENERAL} or {@link #STORE_FILE_SHARED_GENERAL}
+ * @return Instance of {@link InputStream} for migrating data, null if no migration is
+ * necessary.
+ * @throws IllegalArgumentException on invalid storeFileId.
*/
@Nullable
- public static ConfigStoreMigrationData loadFromConfigStore() {
- // Note: OEMs should add code to parse data from their config store format here!
- return null;
+ public static InputStream convertAndRetrieveSharedConfigStoreFile(
+ @SharedStoreFileId int storeFileId) {
+ if (storeFileId != STORE_FILE_SHARED_GENERAL && storeFileId != STORE_FILE_SHARED_SOFTAP) {
+ throw new IllegalArgumentException("Invalid shared store file id");
+ }
+ try {
+ // OEMs should do conversions necessary here before returning the stream.
+ return getSharedAtomicFile(storeFileId).openRead();
+ } catch (FileNotFoundException e) {
+ return null;
+ }
}
/**
- * Remove OEM's config store.
+ * Remove the legacy shared wifi config store file.
+ *
+ * @param storeFileId Identifier for the config store file. One of
+ * {@link #STORE_FILE_SHARED_GENERAL} or {@link #STORE_FILE_SHARED_GENERAL}
+ * @throws IllegalArgumentException on invalid storeFileId.
+ */
+ public static void removeSharedConfigStoreFile(@SharedStoreFileId int storeFileId) {
+ if (storeFileId != STORE_FILE_SHARED_GENERAL && storeFileId != STORE_FILE_SHARED_SOFTAP) {
+ throw new IllegalArgumentException("Invalid shared store file id");
+ }
+ getSharedAtomicFile(storeFileId).delete();
+ }
+
+ /**
+ * Load data from legacy user wifi config store file.
+ * TODO(b/149418926): Add XSD for the AOSP file format for each file from R.
* <p>
* Note:
- * <li>OEMs need to implement {@link #removeConfigStore()} only if their
- * existing config store format or file locations differs from the vanilla AOSP implementation (
- * which is what the wifi mainline module understands).
+ * <li>OEMs need to change the implementation of
+ * {@link #convertAndRetrieveUserConfigStoreFile(int, UserHandle)} only if their existing config
+ * store format or file locations differs from the vanilla AOSP implementation.</li>
+ * <li>The wifi apex will invoke
+ * {@link #convertAndRetrieveUserConfigStoreFile(int, UserHandle)}
+ * method on every bootup, it is the responsibility of the OEM implementation to ensure that
+ * they perform the necessary in place conversion of their config store file to conform to the
+ * AOSP format. The OEM should ensure that the method should only return the
+ * {@link InputStream} stream for the data to be migrated only on the first bootup.</li>
+ * <li>Once the migration is done, the apex will invoke
+ * {@link #removeUserConfigStoreFile(int, UserHandle)} to delete the store file.</li>
+ * <li>The only relevant invocation of
+ * {@link #convertAndRetrieveUserConfigStoreFile(int, UserHandle)} occurs when a previously
+ * released device upgrades to the wifi apex from an OEM implementation of the wifi
+ * stack.
* </li>
- * <li> The wifi mainline module will invoke {@link #removeConfigStore()} after it migrates
- * all the existing data retrieved from {@link #loadFromConfigStore()}.
+ * <li>Ensure that the legacy file paths are accessible to the wifi module (sepolicy rules, file
+ * permissions, etc). Since the wifi service continues to run inside system_server process, this
+ * method will be called from the same context (so ideally the file should still be accessible).
* </li>
+ *
+ * @param storeFileId Identifier for the config store file. One of
+ * {@link #STORE_FILE_USER_GENERAL} or {@link #STORE_FILE_USER_NETWORK_SUGGESTIONS}
+ * @param userHandle User handle.
+ * @return Instance of {@link InputStream} for migrating data, null if no migration is
+ * necessary.
+ * @throws IllegalArgumentException on invalid storeFileId or userHandle.
*/
- public static void removeConfigStore() {
- // Note: OEMs should remove their custom config store files here!
+ @Nullable
+ public static InputStream convertAndRetrieveUserConfigStoreFile(
+ @UserStoreFileId int storeFileId, @NonNull UserHandle userHandle) {
+ if (storeFileId != STORE_FILE_USER_GENERAL
+ && storeFileId != STORE_FILE_USER_NETWORK_SUGGESTIONS) {
+ throw new IllegalArgumentException("Invalid user store file id");
+ }
+ Objects.requireNonNull(userHandle);
+ try {
+ // OEMs should do conversions necessary here before returning the stream.
+ return getUserAtomicFile(storeFileId, userHandle.getIdentifier()).openRead();
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Remove the legacy user wifi config store file.
+ *
+ * @param storeFileId Identifier for the config store file. One of
+ * {@link #STORE_FILE_USER_GENERAL} or {@link #STORE_FILE_USER_NETWORK_SUGGESTIONS}
+ * @param userHandle User handle.
+ * @throws IllegalArgumentException on invalid storeFileId or userHandle.
+ */
+ public static void removeUserConfigStoreFile(
+ @UserStoreFileId int storeFileId, @NonNull UserHandle userHandle) {
+ if (storeFileId != STORE_FILE_USER_GENERAL
+ && storeFileId != STORE_FILE_USER_NETWORK_SUGGESTIONS) {
+ throw new IllegalArgumentException("Invalid user store file id");
+ }
+ Objects.requireNonNull(userHandle);
+ getUserAtomicFile(storeFileId, userHandle.getIdentifier()).delete();
}
/**
@@ -424,7 +487,7 @@
* <li> This is method is invoked once on the first bootup. OEM can safely delete these settings
* once the migration is complete. The first & only relevant invocation of
* {@link #loadFromSettings(Context)} ()} occurs when a previously released
- * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack.
+ * device upgrades to the wifi apex from an OEM implementation of the wifi stack.
* </li>
*
* @param context Context to use for loading the settings provider.
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 6dbb0bd..72ca900 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -545,6 +545,7 @@
mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
: WifiConfiguration.METERED_OVERRIDE_NONE;
wifiConfiguration.carrierId = mCarrierId;
+ wifiConfiguration.trusted = !mIsNetworkUntrusted;
return wifiConfiguration;
}
@@ -684,8 +685,7 @@
mIsAppInteractionRequired,
mIsUserInteractionRequired,
mIsSharedWithUser,
- mIsInitialAutojoinEnabled,
- mIsNetworkUntrusted);
+ mIsInitialAutojoinEnabled);
}
}
@@ -728,13 +728,6 @@
*/
public final boolean isInitialAutoJoinEnabled;
- /**
- * Whether this network will be brought up as untrusted (TRUSTED capability bit removed).
- * @hide
- */
- public final boolean isNetworkUntrusted;
-
-
/** @hide */
public WifiNetworkSuggestion() {
this.wifiConfiguration = new WifiConfiguration();
@@ -743,7 +736,6 @@
this.isUserInteractionRequired = false;
this.isUserAllowedToManuallyConnect = true;
this.isInitialAutoJoinEnabled = true;
- this.isNetworkUntrusted = false;
}
/** @hide */
@@ -752,8 +744,7 @@
boolean isAppInteractionRequired,
boolean isUserInteractionRequired,
boolean isUserAllowedToManuallyConnect,
- boolean isInitialAutoJoinEnabled,
- boolean isNetworkUntrusted) {
+ boolean isInitialAutoJoinEnabled) {
checkNotNull(networkConfiguration);
this.wifiConfiguration = networkConfiguration;
this.passpointConfiguration = passpointConfiguration;
@@ -762,7 +753,6 @@
this.isUserInteractionRequired = isUserInteractionRequired;
this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect;
this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled;
- this.isNetworkUntrusted = isNetworkUntrusted;
}
public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR =
@@ -775,8 +765,7 @@
in.readBoolean(), // isAppInteractionRequired
in.readBoolean(), // isUserInteractionRequired
in.readBoolean(), // isSharedCredentialWithUser
- in.readBoolean(), // isAutojoinEnabled
- in.readBoolean()
+ in.readBoolean() // isAutojoinEnabled
);
}
@@ -799,7 +788,6 @@
dest.writeBoolean(isUserInteractionRequired);
dest.writeBoolean(isUserAllowedToManuallyConnect);
dest.writeBoolean(isInitialAutoJoinEnabled);
- dest.writeBoolean(isNetworkUntrusted);
}
@Override
@@ -842,7 +830,7 @@
.append(", isUserInteractionRequired=").append(isUserInteractionRequired)
.append(", isCredentialSharedWithUser=").append(isUserAllowedToManuallyConnect)
.append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled)
- .append(", isUnTrusted=").append(isNetworkUntrusted)
+ .append(", isUnTrusted=").append(!wifiConfiguration.trusted)
.append(" ]");
return sb.toString();
}
@@ -933,7 +921,7 @@
/** @see Builder#setUntrusted(boolean) */
public boolean isUntrusted() {
- return isNetworkUntrusted;
+ return !wifiConfiguration.trusted;
}
/**
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 5e48919..d299cdc 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -182,7 +182,7 @@
public List<Integer> getAvailableChannels(int band) {
try {
Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mContext.getAttributionTag());
List<Integer> channels = bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA);
return channels == null ? new ArrayList<>() : channels;
} catch (RemoteException e) {
@@ -963,7 +963,7 @@
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
- scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+ scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
}
@@ -984,7 +984,7 @@
validateChannel();
Bundle scanParams = new Bundle();
scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
- scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+ scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams);
}
@@ -1001,7 +1001,7 @@
validateChannel();
Bundle scanParams = new Bundle();
scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
- scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+ scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
Message reply =
mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams);
return reply.what == CMD_OP_SUCCEEDED;
@@ -1056,7 +1056,7 @@
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
- scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+ scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
@@ -1073,7 +1073,7 @@
validateChannel();
Bundle scanParams = new Bundle();
scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
- scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+ scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams);
}
@@ -1086,7 +1086,7 @@
validateChannel();
Bundle scanParams = new Bundle();
scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
- scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getFeatureId());
+ scanParams.putString(REQUEST_FEATURE_ID_KEY, mContext.getAttributionTag());
Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0,
scanParams);
if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 2ebaa18..c2ae17c 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -267,7 +267,7 @@
try {
Binder binder = new Binder();
- mService.connect(binder, mContext.getOpPackageName(), mContext.getFeatureId(),
+ mService.connect(binder, mContext.getOpPackageName(), mContext.getAttributionTag(),
new WifiAwareEventCallbackProxy(this, looper, binder, attachCallback,
identityChangedListener), configRequest,
identityChangedListener != null);
@@ -298,7 +298,7 @@
}
try {
- mService.publish(mContext.getOpPackageName(), mContext.getFeatureId(), clientId,
+ mService.publish(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId,
publishConfig,
new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback,
clientId));
@@ -336,7 +336,7 @@
}
try {
- mService.subscribe(mContext.getOpPackageName(), mContext.getFeatureId(), clientId,
+ mService.subscribe(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId,
subscribeConfig,
new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback,
clientId));
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 65e8b3d..fa806e7 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -1037,18 +1037,7 @@
* @return a Unique identifier for a Credential object
*/
public int getUniqueId() {
- int usedCredential;
-
- // Initialize usedCredential based on the credential type of the profile
- if (mUserCredential != null) {
- usedCredential = 0;
- } else if (mCertCredential != null) {
- usedCredential = 1;
- } else {
- usedCredential = 2;
- }
-
- return Objects.hash(usedCredential, mRealm);
+ return Objects.hash(mUserCredential, mCertCredential, mSimCredential, mRealm);
}
@Override
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index a310ff6..724ccf0 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1180,7 +1180,7 @@
== AsyncChannel.STATUS_SUCCESSFUL) {
Bundle bundle = new Bundle();
bundle.putString(CALLING_PACKAGE, c.mContext.getOpPackageName());
- bundle.putString(CALLING_FEATURE_ID, c.mContext.getFeatureId());
+ bundle.putString(CALLING_FEATURE_ID, c.mContext.getAttributionTag());
bundle.putBinder(CALLING_BINDER, binder);
c.mAsyncChannel.sendMessage(UPDATE_CHANNEL_INFO, 0,
c.putListener(null), bundle);
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index cb0c5d4..865702a 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -146,8 +146,8 @@
Binder binder = new Binder();
try {
- mService.startRanging(binder, mContext.getOpPackageName(), mContext.getFeatureId(),
- workSource, request, new IRttCallback.Stub() {
+ mService.startRanging(binder, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), workSource, request, new IRttCallback.Stub() {
@Override
public void onRangingFailure(int status) throws RemoteException {
clearCallingIdentity();
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 4c22d5d..6cb8324 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -156,8 +156,7 @@
+ "distance: 0(cm), distanceSd: 0(cm), "
+ "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "
+ "standard: 11ac, "
- + "80211mcResponder: is not supported, Carrier AP: no, "
- + "Carrier AP EAP Type: 0, Carrier name: null, "
+ + "80211mcResponder: is not supported, "
+ "Radio Chain Infos: null", scanResult.toString());
}
@@ -179,8 +178,7 @@
+ "distanceSd: 0(cm), "
+ "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "
+ "standard: 11ac, "
- + "80211mcResponder: is not supported, Carrier AP: no, "
- + "Carrier AP EAP Type: 0, Carrier name: null, "
+ + "80211mcResponder: is not supported, "
+ "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, "
+ "RadioChainInfo: id=1, level=-54]", scanResult.toString());
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 060ddf0..1a44270 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -127,8 +127,9 @@
.setMaxNumberOfClients(10)
.setAutoShutdownEnabled(true)
.setShutdownTimeoutMillis(500000)
- .enableClientControlByUser(true)
- .setClientList(testBlockedClientList, testAllowedClientList)
+ .setClientControlByUserEnabled(true)
+ .setBlockedClientList(testBlockedClientList)
+ .setAllowedClientList(testAllowedClientList)
.build();
assertThat(original.getPassphrase()).isEqualTo("secretsecret");
assertThat(original.getSecurityType()).isEqualTo(
@@ -264,7 +265,9 @@
ArrayList<MacAddress> testBlockedClientList = new ArrayList<>();
testBlockedClientList.add(testMacAddress_1);
SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
- configBuilder.setClientList(testBlockedClientList, testAllowedClientList);
+ configBuilder.setBlockedClientList(testBlockedClientList)
+ .setAllowedClientList(testAllowedClientList)
+ .build();
}
@Test
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 51bf738..01b2a8d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -532,7 +532,7 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
- configuration, null, false, true, true, true, false);
+ configuration, null, false, true, true, true);
Parcel parcelW = Parcel.obtain();
suggestion.writeToParcel(parcelW, 0);
@@ -603,14 +603,14 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, true, false, true, true, false);
+ new WifiNetworkSuggestion(configuration, null, true, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, true, true, true, false);
+ new WifiNetworkSuggestion(configuration1, null, false, true, true, true);
assertEquals(suggestion, suggestion1);
assertEquals(suggestion.hashCode(), suggestion1.hashCode());
@@ -626,13 +626,13 @@
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID_1;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
assertNotEquals(suggestion, suggestion1);
}
@@ -648,13 +648,13 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
assertNotEquals(suggestion, suggestion1);
}
@@ -669,13 +669,13 @@
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
assertNotEquals(suggestion, suggestion1);
}
@@ -770,7 +770,7 @@
.setWpa2Passphrase(TEST_PRESHARED_KEY)
.setUntrusted(true)
.build();
- assertTrue(suggestion.isNetworkUntrusted);
+ assertTrue(suggestion.isUntrusted());
assertFalse(suggestion.isUserAllowedToManuallyConnect);
}
@@ -786,7 +786,7 @@
.setPasspointConfig(passpointConfiguration)
.setUntrusted(true)
.build();
- assertTrue(suggestion.isNetworkUntrusted);
+ assertTrue(suggestion.isUntrusted());
assertFalse(suggestion.isUserAllowedToManuallyConnect);
}
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index 0cc76b6..4881200 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -267,7 +267,7 @@
assertNull(messageBundle.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY));
assertEquals(mContext.getOpPackageName(),
messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY));
- assertEquals(mContext.getFeatureId(),
+ assertEquals(mContext.getAttributionTag(),
messageBundle.getParcelable(WifiScanner.REQUEST_FEATURE_ID_KEY));
}
@@ -297,7 +297,7 @@
Bundle messageBundle = (Bundle) message.obj;
assertEquals(mContext.getOpPackageName(),
messageBundle.getParcelable(WifiScanner.REQUEST_PACKAGE_NAME_KEY));
- assertEquals(mContext.getFeatureId(),
+ assertEquals(mContext.getAttributionTag(),
messageBundle.getParcelable(WifiScanner.REQUEST_FEATURE_ID_KEY));
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index c682582..829d8f0 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.net.wifi.EAPConstants;
@@ -551,4 +552,68 @@
public void validateTwoCertificateDifferent() {
assertFalse(Credential.isX509CertificateEquals(FakeKeys.CA_CERT0, FakeKeys.CA_CERT1));
}
+
+ /**
+ * Verify that unique identifiers are the same for objects with the same credentials
+ */
+ @Test
+ public void testUniqueIdSameCredentialTypes() throws Exception {
+ assertEquals(createCredentialWithSimCredential().getUniqueId(),
+ createCredentialWithSimCredential().getUniqueId());
+ assertEquals(createCredentialWithCertificateCredential().getUniqueId(),
+ createCredentialWithCertificateCredential().getUniqueId());
+ assertEquals(createCredentialWithUserCredential().getUniqueId(),
+ createCredentialWithUserCredential().getUniqueId());
+ }
+
+ /**
+ * Verify that unique identifiers are different for each credential
+ */
+ @Test
+ public void testUniqueIdDifferentForDifferentCredentialTypes() throws Exception {
+ Credential simCred = createCredentialWithSimCredential();
+ Credential certCred = createCredentialWithCertificateCredential();
+ Credential userCred = createCredentialWithUserCredential();
+
+ assertNotEquals(simCred.getUniqueId(), userCred.getUniqueId());
+ assertNotEquals(simCred.getUniqueId(), certCred.getUniqueId());
+ assertNotEquals(certCred.getUniqueId(), userCred.getUniqueId());
+ }
+
+ /**
+ * Verify that unique identifiers are different for a credential with different values
+ */
+ @Test
+ public void testUniqueIdDifferentForSimCredentialsWithDifferentValues() throws Exception {
+ Credential simCred1 = createCredentialWithSimCredential();
+ Credential simCred2 = createCredentialWithSimCredential();
+ simCred2.getSimCredential().setImsi("567890*");
+
+ assertNotEquals(simCred1.getUniqueId(), simCred2.getUniqueId());
+ }
+
+ /**
+ * Verify that unique identifiers are different for a credential with different values
+ */
+ @Test
+ public void testUniqueIdDifferentForUserCredentialsWithDifferentValues() throws Exception {
+ Credential userCred1 = createCredentialWithUserCredential();
+ Credential userCred2 = createCredentialWithUserCredential();
+ userCred2.getUserCredential().setUsername("anotheruser");
+
+ assertNotEquals(userCred1.getUniqueId(), userCred2.getUniqueId());
+ }
+
+ /**
+ * Verify that unique identifiers are different for a credential with different values
+ */
+ @Test
+ public void testUniqueIdDifferentForCertCredentialsWithDifferentValues() throws Exception {
+ Credential certCred1 = createCredentialWithCertificateCredential();
+ Credential certCred2 = createCredentialWithCertificateCredential();
+ certCred2.getCertCredential().setCertSha256Fingerprint(
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CA_CERT0.getEncoded()));
+
+ assertNotEquals(certCred1.getUniqueId(), certCred2.getUniqueId());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index a9dcde0..e6eae41 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -71,7 +71,7 @@
mMockLooperExecutor = mMockLooper.getNewExecutor();
when(mockContext.getOpPackageName()).thenReturn(packageName);
- when(mockContext.getFeatureId()).thenReturn(featureId);
+ when(mockContext.getAttributionTag()).thenReturn(featureId);
}
/**