diff options
483 files changed, 13649 insertions, 5242 deletions
diff --git a/Android.bp b/Android.bp index 9c1a0855ec98..8adf48dc49b6 100644 --- a/Android.bp +++ b/Android.bp @@ -546,7 +546,6 @@ java_library { // 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 @@ filegroup { "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 { ], } +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", @@ -974,7 +986,6 @@ filegroup { 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 +1252,6 @@ filegroup { "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", diff --git a/StubLibraries.bp b/StubLibraries.bp index 7f23df74d2da..da9f1654d60f 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -41,7 +41,7 @@ packages_to_document = [ ] 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 { } 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 @@ stubs_defaults { // 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 @@ module_libs = " " + 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 { 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 { 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 { 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 @@ java_library_static { 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 @@ java_library_static { 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 { 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 02df5e2b6c31..23f025b0a759 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 @@ public class BlobStorePerfTests { } 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 c458d1190e51..e042782af366 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 @@ public class UserLifecycleTests { 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/apex/Android.bp b/apex/Android.bp index 151091137c9c..39137fb908d2 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -49,7 +49,7 @@ stubs_defaults { 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", } @@ -62,7 +62,7 @@ stubs_defaults { 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", } @@ -70,7 +70,7 @@ stubs_defaults { 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 9746dd023002..80062d5d245f 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 @@ public final class BlobInfo implements Parcelable { 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 @@ public final class BlobInfo implements Parcelable { return mLabel; } - public List<AccessorInfo> getAccessors() { - return Collections.unmodifiableList(mAccessors); + public List<LeaseInfo> getLeases() { + return Collections.unmodifiableList(mLeaseInfos); } @Override @@ -70,7 +70,7 @@ public final class BlobInfo implements Parcelable { dest.writeLong(mId); dest.writeLong(mExpiryTimeMs); dest.writeCharSequence(mLabel); - dest.writeList(mAccessors); + dest.writeList(mLeaseInfos); } @Override @@ -83,7 +83,7 @@ public final class BlobInfo implements Parcelable { + "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 814ab6dbd7fd..c339351759cd 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.CurrentTimeMillisLong; 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 @@ public class BlobStoreManager { } /** + * 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 e78381359b41..20c15ab57496 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 @@ package android.app.blob; 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 @@ interface IBlobStoreManager { 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 000000000000..908885731bb1 --- /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 index 3725ad4a6c09..fef50c9e8dba 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 @@ public final class AccessorInfo implements Parcelable { @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 @@ public final class AccessorInfo implements Parcelable { 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 @@ public final class AccessorInfo implements Parcelable { } @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 970766d2c8a6..8b640ca75698 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 static com.android.server.blob.BlobStoreUtils.getPackageResources; 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 @@ class BlobMetadata { 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 53a97cefa59b..f4b8f0f39e85 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.IntRange; 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 @@ public class BlobStoreManagerService extends SystemService { 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 @@ public class BlobStoreManagerService extends SystemService { } } + 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 @@ public class BlobStoreManagerService extends SystemService { 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 @@ public class BlobStoreManagerService extends SystemService { 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 @@ public class BlobStoreManagerService extends SystemService { } @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 6af540acd6a4..fabce766c237 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 @@ class BlobStoreUtils { @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/api/current.txt b/api/current.txt index a85d632e0f31..da9fd09cf152 100644 --- a/api/current.txt +++ b/api/current.txt @@ -290,6 +290,7 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android.app { 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(); @@ -6430,7 +6432,7 @@ package android.app { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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); @@ -6982,9 +6984,9 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.content { 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 @@ package android.content { 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 @@ package android.content { 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 @@ package android.content { 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(); @@ -11439,6 +11441,7 @@ package android.content.pm { 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(); @@ -11893,6 +11896,7 @@ package android.content.pm { 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(); @@ -12022,6 +12026,8 @@ package android.content.pm { 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(); @@ -12046,6 +12052,7 @@ package android.content.pm { 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>); @@ -17352,7 +17359,7 @@ package android.hardware.camera2 { 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; @@ -20094,8 +20101,7 @@ package android.icu.number { 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); @@ -20118,8 +20124,7 @@ package android.icu.number { 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); } @@ -27046,7 +27051,7 @@ package android.media { 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(); @@ -37155,12 +37160,10 @@ package android.os { 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 { @@ -40716,6 +40719,7 @@ package android.provider { 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"; @@ -43014,12 +43018,12 @@ package android.service.autofill { } 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); @@ -48994,8 +48998,8 @@ package android.telephony.ims { 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); } } @@ -53603,10 +53607,11 @@ package android.view { 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 { @@ -55581,7 +55586,7 @@ package android.view { 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); @@ -56739,6 +56744,7 @@ package android.view.contentcapture { 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); diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 69b52693aa46..ee997f154299 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -29,7 +29,7 @@ package android.net { 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 8537b21eb438..077c915ef2dd 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.app.usage { 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 b7aa9da83c6a..d0622d29645d 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -235,6 +235,7 @@ package android { 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"; @@ -407,7 +408,6 @@ package android.app { 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 +448,37 @@ package android.app { 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 +514,7 @@ package android.app { 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 +523,9 @@ package android.app { 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 +546,8 @@ package android.app { 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 +577,13 @@ package android.app { 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 +688,7 @@ package android.app { 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 +757,6 @@ package android.app { 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 +1370,6 @@ package android.app.role { 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); @@ -2133,6 +2131,7 @@ package android.content.pm { 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 +2215,7 @@ package android.content.pm { 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 +2280,6 @@ package android.content.pm { 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 @@ -5488,7 +5486,7 @@ package android.media.tv.tuner.frontend { 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(); @@ -5516,7 +5514,7 @@ package android.media.tv.tuner.frontend { public static class DvbcFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.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 @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); @@ -5782,9 +5780,9 @@ package android.media.tv.tuner.frontend { 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(); @@ -6090,7 +6088,7 @@ package android.net { 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); @@ -6099,7 +6097,7 @@ package android.net { 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"; @@ -6287,7 +6285,6 @@ package android.net { 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); @@ -6301,23 +6298,17 @@ package android.net { 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; @@ -6326,18 +6317,14 @@ package android.net { 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(); @@ -6362,27 +6349,9 @@ package android.net { 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); @@ -8225,13 +8194,15 @@ package android.os { 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 { @@ -8941,11 +8912,13 @@ package android.permission { 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>); @@ -9366,7 +9339,6 @@ package android.provider { 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"; @@ -9379,7 +9351,6 @@ package android.provider { 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 @@ -9426,9 +9397,7 @@ package android.provider { 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"; @@ -9797,7 +9766,7 @@ package android.service.autofill { 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 { @@ -11539,9 +11508,7 @@ package android.telephony { } 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); @@ -11565,19 +11532,16 @@ package android.telephony { 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 java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); 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(); @@ -11604,12 +11568,10 @@ package android.telephony { 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(); @@ -11622,7 +11584,6 @@ package android.telephony { 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(); @@ -11644,13 +11605,9 @@ package android.telephony { 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); @@ -11700,10 +11657,6 @@ package android.telephony { 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"; @@ -12325,10 +12278,6 @@ package android.telephony.ims { 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; @@ -12594,82 +12543,12 @@ package android.telephony.ims { 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 { @@ -12678,56 +12557,6 @@ package android.telephony.ims { 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; } @@ -12974,24 +12803,12 @@ package android.telephony.ims.stub { 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 } } @@ -13196,6 +13013,7 @@ package android.view.contentcapture { 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(); @@ -13206,6 +13024,7 @@ package android.view.contentcapture { 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-current.txt b/api/test-current.txt index 4f4c82b43e70..08888d7700d0 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -83,6 +83,7 @@ package android.app { method public static void resumeAppSwitches() throws android.os.RemoteException; method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int); method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle); + method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String); field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7 field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1 field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6 @@ -263,14 +264,37 @@ package android.app { 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 +335,7 @@ package android.app { 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 +344,9 @@ package android.app { 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 +367,8 @@ package android.app { 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 +398,13 @@ package android.app { 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 +466,7 @@ package android.app { 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 +597,22 @@ package android.app.backup { 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 +857,7 @@ package android.content.integrity { 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 +934,7 @@ package android.content.pm { } 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(); } @@ -3134,7 +3150,7 @@ package android.service.autofill { 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 { @@ -3419,13 +3435,15 @@ package android.service.notification { 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(); @@ -3745,7 +3763,7 @@ package android.telephony { 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 @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); @@ -4019,10 +4037,6 @@ package android.telephony.ims { 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; @@ -4284,82 +4298,12 @@ package android.telephony.ims { 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 { @@ -4368,56 +4312,6 @@ package android.telephony.ims { 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; } @@ -4664,24 +4558,12 @@ package android.telephony.ims.stub { 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 } } @@ -4953,8 +4835,8 @@ package android.view { } 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 { @@ -4970,7 +4852,7 @@ package android.view { 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); @@ -5084,6 +4966,7 @@ package android.view.contentcapture { 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(); @@ -5094,6 +4977,7 @@ package android.view.contentcapture { 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/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 56f2340933d3..713e92354fb4 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -485,7 +485,7 @@ message Atom { 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"]; @@ -3332,16 +3332,12 @@ message ForegroundServiceAppOpSessionEnded { 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 { @@ -7571,8 +7567,8 @@ message AppOps { // 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) @@ -7603,21 +7599,22 @@ message AppOps { } /** - * 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) @@ -8476,9 +8473,11 @@ message RuntimeAppOpAccess { // 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; @@ -8487,6 +8486,7 @@ message RuntimeAppOpAccess { DEFAULT = 0; UNIFORM = 1; RARELY_USED = 2; + BOOT_TIME_SAMPLING = 3; } // sampling strategy used to collect this message diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 258f84d0fb79..b515d0a5b72f 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -350,17 +350,19 @@ bool LogEvent::write(const AttributionNodeInternal& node) { 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 @@ void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last) { 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 @@ void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last) { 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 @@ void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last) { // 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 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) { 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 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) { 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::getTypeId(uint8_t typeInfo) { } 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 b68eeb8d6499..6537f13c4089 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -232,14 +232,15 @@ private: */ 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/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index ca37e9b107a0..2c41e8d0925a 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -18,6 +18,7 @@ package android.animation; 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 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio private float mDurationScale = -1f; /** + * Animation handler used to schedule updates for this animation. + */ + private AnimationHandler mAnimationHandler; + + /** * Public constants */ @@ -1684,6 +1690,15 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * @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 8d6bc7222abd..6480a6abeb78 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5829,7 +5829,7 @@ public class Activity extends ContextThemeWrapper 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 @@ public class Activity extends ContextThemeWrapper 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 dd4788e1146a..1a92b758ab9f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4243,6 +4243,7 @@ public class ActivityManager { * @hide */ @SystemApi + @TestApi @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 ec110435d95c..489a0de0518e 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -34,6 +34,7 @@ import android.os.TransactionTooLargeException; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Activity manager local system service interface. @@ -124,6 +125,12 @@ public abstract class ActivityManagerInternal { 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(); diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md new file mode 100644 index 000000000000..bee701addca8 --- /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 9925d69b9ce0..fa4aa19d846e 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -160,8 +160,8 @@ import java.util.function.Supplier; * <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 @@ -696,6 +696,10 @@ public class AppOpsManager { 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 @@ -704,7 +708,8 @@ public class AppOpsManager { @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 {} @@ -1071,9 +1076,17 @@ public class AppOpsManager { /** @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"; @@ -1356,7 +1369,6 @@ public class AppOpsManager { @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"; @@ -1373,6 +1385,10 @@ public class AppOpsManager { 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"; @@ -1463,6 +1479,7 @@ public class AppOpsManager { OP_LOADER_USAGE_STATS, OP_ACCESS_CALL_AUDIO, OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, + OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, }; /** @@ -1572,6 +1589,7 @@ public class AppOpsManager { 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 }; /** @@ -1676,6 +1694,7 @@ public class AppOpsManager { OPSTR_LOADER_USAGE_STATS, OPSTR_ACCESS_CALL_AUDIO, OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, + OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER, }; /** @@ -1781,6 +1800,7 @@ public class AppOpsManager { "LOADER_USAGE_STATS", "ACCESS_CALL_AUDIO", "AUTO_REVOKE_PERMISSIONS_IF_UNUSED", + "AUTO_REVOKE_MANAGED_BY_INSTALLER", }; /** @@ -1887,6 +1907,7 @@ public class AppOpsManager { 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 }; /** @@ -1993,6 +2014,7 @@ public class AppOpsManager { null, // LOADER_USAGE_STATS null, // ACCESS_CALL_AUDIO null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED + null, // AUTO_REVOKE_MANAGED_BY_INSTALLER }; /** @@ -2098,6 +2120,7 @@ public class AppOpsManager { null, // LOADER_USAGE_STATS null, // ACCESS_CALL_AUDIO null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED + null, // AUTO_REVOKE_MANAGED_BY_INSTALLER }; /** @@ -2202,6 +2225,7 @@ public class AppOpsManager { 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 }; /** @@ -2310,9 +2334,118 @@ public class AppOpsManager { 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<>(); @@ -2353,6 +2486,10 @@ public class AppOpsManager { 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); @@ -2437,6 +2574,15 @@ public class AppOpsManager { } /** + * Retrieve a logging id for the operation. + * + * @hide + */ + public static int opToLoggingId(int op) { + return sOpToLoggingId[op]; + } + + /** * @hide */ public static int strDebugOpToOp(String op) { @@ -2658,23 +2804,23 @@ public class AppOpsManager { 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; } @@ -2699,21 +2845,21 @@ public class AppOpsManager { * 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 } @@ -2727,7 +2873,7 @@ public class AppOpsManager { public OpEventProxyInfo(@NonNull OpEventProxyInfo orig) { mUid = orig.mUid; mPackageName = orig.mPackageName; - mFeatureId = orig.mFeatureId; + mAttributionTag = orig.mAttributionTag; } /** @@ -2747,11 +2893,11 @@ public class AppOpsManager { } /** - * 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 @@ -2762,11 +2908,11 @@ public class AppOpsManager { 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 @@ -2783,14 +2929,14 @@ public class AppOpsManager { 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 } @@ -2814,7 +2960,7 @@ public class AppOpsManager { 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() {} */ @@ -3013,7 +3159,7 @@ public class AppOpsManager { /** * 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 */ @@ -3022,7 +3168,7 @@ public class AppOpsManager { @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 */ @@ -3321,8 +3467,8 @@ public class AppOpsManager { } /** - * 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 * @@ -3339,7 +3485,7 @@ public class AppOpsManager { /** * 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 * @@ -3357,7 +3503,7 @@ public class AppOpsManager { /** * 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 * @@ -3374,8 +3520,8 @@ public class AppOpsManager { } /** - * 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) @@ -3450,7 +3596,7 @@ public class AppOpsManager { /** - * Creates a new OpFeatureEntry. + * Creates a new OpAttributionEntry. * * @param op * The code of the op @@ -3463,7 +3609,7 @@ public class AppOpsManager { * @hide */ @DataClass.Generated.Member - public OpFeatureEntry( + public AttributedOpEntry( @IntRange(from = 0, to = _NUM_OP - 1) int op, boolean running, @Nullable LongSparseArray<NoteOpEvent> accessEvents, @@ -3533,7 +3679,7 @@ public class AppOpsManager { /** @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) { ... } @@ -3556,16 +3702,16 @@ public class AppOpsManager { } @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); } }; @@ -3574,7 +3720,7 @@ public class AppOpsManager { 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() {} */ @@ -3600,8 +3746,8 @@ public class AppOpsManager { 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 @@ -3642,7 +3788,7 @@ public class AppOpsManager { * @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); @@ -3659,7 +3805,7 @@ public class AppOpsManager { * @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), @@ -3677,7 +3823,7 @@ public class AppOpsManager { * @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, @@ -3694,13 +3840,14 @@ public class AppOpsManager { 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); - - if (lastAccessEvent == null || (lastFeatureAccessEvent != null - && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) { - lastAccessEvent = lastFeatureAccessEvent; + for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) { + NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastAccessEvent( + fromUidState, toUidState, flags); + + if (lastAccessEvent == null || (lastAttributionAccessEvent != null + && lastAttributionAccessEvent.getNoteTime() + > lastAccessEvent.getNoteTime())) { + lastAccessEvent = lastAttributionAccessEvent; } } @@ -3720,7 +3867,7 @@ public class AppOpsManager { * @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) { @@ -3756,7 +3903,7 @@ public class AppOpsManager { * @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); @@ -3773,7 +3920,7 @@ public class AppOpsManager { * @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), @@ -3791,7 +3938,7 @@ public class AppOpsManager { * @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, @@ -3808,13 +3955,14 @@ public class AppOpsManager { 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); - - if (lastAccessEvent == null || (lastFeatureAccessEvent != null - && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) { - lastAccessEvent = lastFeatureAccessEvent; + for (AttributedOpEntry attributionEntry : mAttributedOpEntries.values()) { + NoteOpEvent lastAttributionAccessEvent = attributionEntry.getLastRejectEvent( + fromUidState, toUidState, flags); + + if (lastAccessEvent == null || (lastAttributionAccessEvent != null + && lastAttributionAccessEvent.getNoteTime() + > lastAccessEvent.getNoteTime())) { + lastAccessEvent = lastAttributionAccessEvent; } } @@ -3835,7 +3983,7 @@ public class AppOpsManager { * @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) { @@ -3851,8 +3999,8 @@ public class AppOpsManager { * @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; } } @@ -3878,7 +4026,7 @@ public class AppOpsManager { * @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); @@ -3894,7 +4042,7 @@ public class AppOpsManager { * @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), @@ -3911,7 +4059,7 @@ public class AppOpsManager { * @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, @@ -3930,7 +4078,7 @@ public class AppOpsManager { * @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) { @@ -4005,7 +4153,7 @@ public class AppOpsManager { * @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); @@ -4022,7 +4170,7 @@ public class AppOpsManager { * @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), @@ -4040,7 +4188,7 @@ public class AppOpsManager { * @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, @@ -4060,7 +4208,7 @@ public class AppOpsManager { * @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) { @@ -4094,15 +4242,15 @@ public class AppOpsManager { * 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, @@ -4111,9 +4259,9 @@ public class AppOpsManager { 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 } @@ -4127,14 +4275,14 @@ public class AppOpsManager { } /** - * 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 @@ -4145,7 +4293,7 @@ public class AppOpsManager { dest.writeInt(mOp); dest.writeInt(mMode); - dest.writeMap(mFeatures); + dest.writeMap(mAttributedOpEntries); } @Override @@ -4161,8 +4309,8 @@ public class AppOpsManager { 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( @@ -4172,9 +4320,9 @@ public class AppOpsManager { 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 } @@ -4198,7 +4346,7 @@ public class AppOpsManager { 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() {} */ @@ -4214,7 +4362,7 @@ public class AppOpsManager { 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); } @@ -4227,7 +4375,7 @@ public class AppOpsManager { @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 {} @@ -4247,11 +4395,11 @@ public class AppOpsManager { 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. @@ -4272,7 +4420,7 @@ public class AppOpsManager { 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; @@ -4280,12 +4428,12 @@ public class AppOpsManager { 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; @@ -4303,7 +4451,7 @@ public class AppOpsManager { 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; @@ -4367,14 +4515,14 @@ public class AppOpsManager { } /** - * 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; } @@ -4425,7 +4573,7 @@ public class AppOpsManager { * @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); } } @@ -4585,7 +4733,7 @@ public class AppOpsManager { * * @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. @@ -4593,7 +4741,7 @@ public class AppOpsManager { * * @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(); @@ -4607,7 +4755,7 @@ public class AppOpsManager { 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); } @@ -4638,28 +4786,28 @@ public class AppOpsManager { /** @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 */ @@ -4939,7 +5087,7 @@ public class AppOpsManager { } } - 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(); @@ -4949,8 +5097,8 @@ public class AppOpsManager { 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); } } @@ -4969,24 +5117,24 @@ public class AppOpsManager { } 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); } /** @@ -5131,7 +5279,7 @@ public class AppOpsManager { @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) { @@ -5140,70 +5288,71 @@ public class AppOpsManager { 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); } } } @@ -5211,38 +5360,38 @@ public class AppOpsManager { 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); } @@ -5255,17 +5404,18 @@ public class AppOpsManager { 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; } /** @@ -5276,13 +5426,13 @@ public class AppOpsManager { */ 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; } @@ -5295,7 +5445,7 @@ public class AppOpsManager { /** * 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. @@ -5303,13 +5453,13 @@ public class AppOpsManager { */ 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 { @@ -5326,25 +5476,25 @@ public class AppOpsManager { /** * 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); } } } @@ -5360,7 +5510,7 @@ public class AppOpsManager { @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 = @@ -5388,11 +5538,11 @@ public class AppOpsManager { 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; @@ -5401,58 +5551,58 @@ public class AppOpsManager { @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 */ @@ -5462,20 +5612,20 @@ public class AppOpsManager { @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); @@ -5487,15 +5637,15 @@ public class AppOpsManager { } } - 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<>(); @@ -5506,7 +5656,7 @@ public class AppOpsManager { 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); @@ -5603,7 +5753,7 @@ public class AppOpsManager { } 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); @@ -5639,46 +5789,46 @@ public class AppOpsManager { /** - * 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); } @@ -5689,7 +5839,7 @@ public class AppOpsManager { // 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; } @@ -5701,10 +5851,10 @@ public class AppOpsManager { // 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); } @@ -5715,35 +5865,35 @@ public class AppOpsManager { /** @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); } }; @@ -5752,7 +5902,7 @@ public class AppOpsManager { 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() {} */ @@ -5886,6 +6036,11 @@ public class AppOpsManager { return mOp; } + /** @hide */ + public int getLoggingOpCode() { + return AppOpsManager.opToLoggingId(mOp); + } + /** * Gets the number times the op was accessed (performed) in the foreground. * @@ -6428,7 +6583,7 @@ public class AppOpsManager { 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); @@ -6468,8 +6623,9 @@ public class AppOpsManager { 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 { @@ -7059,8 +7215,8 @@ public class AppOpsManager { * @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 @@ -7070,8 +7226,8 @@ public class AppOpsManager { * @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); } /** @@ -7087,7 +7243,8 @@ public class AppOpsManager { * @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 @@ -7098,9 +7255,9 @@ public class AppOpsManager { * * @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)); } @@ -7135,8 +7292,8 @@ public class AppOpsManager { * @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 @@ -7144,8 +7301,8 @@ public class AppOpsManager { * 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); } /** @@ -7155,7 +7312,8 @@ public class AppOpsManager { * @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 @@ -7165,7 +7323,7 @@ public class AppOpsManager { * @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); @@ -7176,14 +7334,14 @@ public class AppOpsManager { } } - 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); } } @@ -7223,8 +7381,8 @@ public class AppOpsManager { * @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} @@ -7236,8 +7394,8 @@ public class AppOpsManager { * @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() @@ -7256,8 +7414,8 @@ public class AppOpsManager { * @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} @@ -7267,8 +7425,8 @@ public class AppOpsManager { * 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); } @@ -7299,14 +7457,14 @@ public class AppOpsManager { * @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); } /** @@ -7317,14 +7475,14 @@ public class AppOpsManager { * @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 { @@ -7338,17 +7496,17 @@ public class AppOpsManager { } 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); } } @@ -7537,8 +7695,8 @@ public class AppOpsManager { * @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 @@ -7549,8 +7707,8 @@ public class AppOpsManager { * 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); } /** @@ -7559,7 +7717,8 @@ public class AppOpsManager { * @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 * @@ -7573,8 +7732,8 @@ public class AppOpsManager { * @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)); @@ -7617,7 +7776,8 @@ public class AppOpsManager { * @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 @@ -7625,8 +7785,8 @@ public class AppOpsManager { * 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); } /** @@ -7636,7 +7796,8 @@ public class AppOpsManager { * @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 * @@ -7647,7 +7808,7 @@ public class AppOpsManager { * @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); @@ -7659,13 +7820,13 @@ public class AppOpsManager { } 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); } } @@ -7699,8 +7860,8 @@ public class AppOpsManager { * 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); } /** @@ -7721,9 +7882,9 @@ public class AppOpsManager { * @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(); } @@ -7834,15 +7995,15 @@ public class AppOpsManager { * 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)); } /** @@ -7851,9 +8012,9 @@ public class AppOpsManager { * <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} @@ -7863,16 +8024,16 @@ public class AppOpsManager { 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); } } @@ -7953,10 +8114,10 @@ public class AppOpsManager { 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]); @@ -7974,10 +8135,10 @@ public class AppOpsManager { * @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(); @@ -7989,13 +8150,13 @@ public class AppOpsManager { 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 87d33a980438..18df401c9c8d 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -840,6 +840,27 @@ public class ApplicationPackageManager extends PackageManager { } @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 { @@ -3337,6 +3358,15 @@ public class ApplicationPackageManager extends PackageManager { } } + @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 4d955dbe8703..b0c2762c3439 100644 --- a/core/java/android/app/AsyncNotedAppOp.java +++ b/core/java/android/app/AsyncNotedAppOp.java @@ -48,8 +48,8 @@ public final class AsyncNotedAppOp implements Parcelable { /** 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 @@ public final class AsyncNotedAppOp implements Parcelable { * 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 final class AsyncNotedAppOp implements Parcelable { 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 @@ public final class AsyncNotedAppOp implements Parcelable { 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 @@ public final class AsyncNotedAppOp implements Parcelable { } /** - * {@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 @@ public final class AsyncNotedAppOp implements Parcelable { 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 @@ public final class AsyncNotedAppOp implements Parcelable { 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 @@ public final class AsyncNotedAppOp implements Parcelable { // 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 @@ public final class AsyncNotedAppOp implements Parcelable { 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 @@ public final class AsyncNotedAppOp implements Parcelable { 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 @@ public final class AsyncNotedAppOp implements Parcelable { }; @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 2873b10e60c8..9ccfe8df14c2 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -219,8 +219,8 @@ class ContextImpl extends Context { @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 @@ class ContextImpl extends Context { /** @hide */ @Override - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getAttributionTag() { + return mAttributionTag; } @Override @@ -1026,10 +1026,10 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { } 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { } 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 @@ class ContextImpl extends Context { } @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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { } 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 @@ class ContextImpl extends Context { } mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName; - mFeatureId = featureId; + mAttributionTag = attributionTag; mContentResolver = new ApplicationContentResolver(this, mainThread); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 18932c6b0784..818a12145932 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1720,11 +1720,10 @@ public class Instrumentation { 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 @@ public class Instrumentation { 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 @@ public class Instrumentation { 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 @@ public class Instrumentation { 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 @@ public class Instrumentation { 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 32e7d84e6083..864af3d4ae4d 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; 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 @@ public class Notification implements Parcelable 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 @@ public class Notification implements Parcelable 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 @@ public class Notification implements Parcelable } 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 @@ public class Notification implements Parcelable } 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 class Notification implements Parcelable 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 @@ public class Notification implements Parcelable */ @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 @@ public class Notification implements Parcelable 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 @@ public class Notification implements Parcelable 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 @@ public class Notification implements Parcelable */ @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/PendingIntent.java b/core/java/android/app/PendingIntent.java index f68c929144c8..792f840638a9 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -355,8 +355,8 @@ public final class PendingIntent implements Parcelable { 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 @@ public final class PendingIntent implements Parcelable { 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 @@ public final class PendingIntent implements Parcelable { 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 @@ public final class PendingIntent implements Parcelable { 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 @@ public final class PendingIntent implements Parcelable { 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 @@ public final class PendingIntent implements Parcelable { 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/RuntimeAppOpAccessMessage.java b/core/java/android/app/RuntimeAppOpAccessMessage.java index a81b8e7ab13c..a19f815c4298 100644 --- a/core/java/android/app/RuntimeAppOpAccessMessage.java +++ b/core/java/android/app/RuntimeAppOpAccessMessage.java @@ -44,7 +44,7 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { /** 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 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { * 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 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { @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 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { 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 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { } /** - * 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 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { // 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 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { 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 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { 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 @@ public final class RuntimeAppOpAccessMessage implements Parcelable { 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/Service.java b/core/java/android/app/Service.java index dc8269f900b7..81396fe6a270 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -34,6 +34,7 @@ import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.view.contentcapture.ContentCaptureManager; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -306,7 +307,8 @@ import java.lang.annotation.RetentionPolicy; * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.java * bind} */ -public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { +public abstract class Service extends ContextWrapper implements ComponentCallbacks2, + ContentCaptureManager.ContentCaptureClient { private static final String TAG = "Service"; /** @@ -817,8 +819,16 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac writer.println("nothing to dump"); } + @Override + protected void attachBaseContext(Context newBase) { + super.attachBaseContext(newBase); + if (newBase != null) { + newBase.setContentCaptureOptions(getContentCaptureOptions()); + } + } + // ------------------ Internal API ------------------ - + /** * @hide */ @@ -835,6 +845,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac mActivityManager = (IActivityManager)activityManager; mStartCompatibility = getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.ECLAIR; + setContentCaptureOptions(application.getContentCaptureOptions()); } /** @@ -849,6 +860,18 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac return mClassName; } + /** @hide */ + @Override + public final ContentCaptureManager.ContentCaptureClient getContentCaptureClient() { + return this; + } + + /** @hide */ + @Override + public final ComponentName contentCaptureClientGetComponentName() { + return new ComponentName(this, mClassName); + } + // set by the thread after the constructor and before onCreate(Bundle icicle) is called. @UnsupportedAppUsage private ActivityThread mThread = null; diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 42563b5a1561..1329fa45bb50 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -399,7 +399,6 @@ public class StatusBarManager { * {@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 13b90ca6ced1..0a880dc0cbb7 100644 --- a/core/java/android/app/SyncNotedAppOp.java +++ b/core/java/android/app/SyncNotedAppOp.java @@ -43,24 +43,24 @@ public final class SyncNotedAppOp implements Parcelable { /** 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 @@ public final class SyncNotedAppOp implements Parcelable { /** - * 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 @@ public final class SyncNotedAppOp implements Parcelable { //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 @@ public final class SyncNotedAppOp implements Parcelable { 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 @@ public final class SyncNotedAppOp implements Parcelable { // 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 @@ public final class SyncNotedAppOp implements Parcelable { 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 @@ public final class SyncNotedAppOp implements Parcelable { 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 a3bcc9cee39b..e8f30df614f3 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -19,6 +19,7 @@ package android.app; 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 @@ public final class SystemServiceRegistry { 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 @@ public final class SystemServiceRegistry { @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 1a845476e108..b2dd0ef012d8 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; 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 class TaskInfo { */ 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 @@ public class TaskInfo { 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 @@ public class TaskInfo { 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 @@ public class TaskInfo { + " 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 345eaae597d5..1b1568ac5a12 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -478,7 +478,7 @@ public class WallpaperManager { 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 @@ public class WallpaperManager { 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) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c532279676a0..bc8d05e0810e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -5215,6 +5215,10 @@ public class DevicePolicyManager { * <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. @@ -6001,7 +6005,7 @@ public class DevicePolicyManager { * @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 +6023,7 @@ public class DevicePolicyManager { /** * @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 +6053,10 @@ public class DevicePolicyManager { * @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 +6068,10 @@ public class DevicePolicyManager { * @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 +6094,11 @@ public class DevicePolicyManager { * @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 +6110,11 @@ public class DevicePolicyManager { * @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 +8808,11 @@ public class DevicePolicyManager { * <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 +11852,19 @@ public class DevicePolicyManager { } /** - * 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 +11872,16 @@ public class DevicePolicyManager { } /** - * 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(); } @@ -11984,17 +11990,17 @@ public class DevicePolicyManager { * 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 + * @param timeoutMillis Maximum time the profile is allowed to be off in milliseconds or 0 if * not limited. * @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 da48663145e1..514677e4867d 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -308,11 +308,11 @@ interface IDevicePolicyManager { 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 @@ interface IDevicePolicyManager { 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 db4f1de1f743..917eeb84c7a5 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -636,7 +636,6 @@ public final class RoleManager { * @hide */ @Nullable - @SystemApi public String getDefaultSmsPackage(@UserIdInt int userId) { try { return mService.getDefaultSmsPackage(userId); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 6ae68fcad6f4..608b563bfc76 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -849,7 +849,7 @@ public final class BluetoothAdapter { synchronized (mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } return sBluetoothLeScanner; @@ -1663,11 +1663,11 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 7ff64663349b..3b4fe0a30b80 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -62,7 +62,7 @@ public final class BluetoothManager { * @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/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index bd3298c79fff..d8e8b27d0621 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -230,12 +230,12 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall // 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } } - 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall /** {@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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** - * 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** + * @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 a9b786217eef..d0f5ec467458 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -80,7 +80,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { mContentResolver = contentResolver; mContentProvider = contentProvider; mPackageName = contentResolver.mPackageName; - mFeatureId = contentResolver.mFeatureId; + mAttributionTag = contentResolver.mAttributionTag; mAuthority = authority; mStable = stable; @@ -195,7 +195,8 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 31e1fc824ed2..b134c3796b40 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -767,7 +767,7 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { } /** @hide */ - public @Nullable String getFeatureId() { - return mFeatureId; + public @Nullable String getAttributionTag() { + return mAttributionTag; } @UnsupportedAppUsage @@ -3757,7 +3760,7 @@ public abstract class ContentResolver implements ContentInterface { @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 5e9ed6026083..318ae11759db 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -815,16 +815,25 @@ public abstract class Context { } /** - * <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(); @@ -5826,19 +5835,29 @@ public abstract class Context { } /** - * 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 b7e04cf74c4a..d389d2a4ae09 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -164,8 +164,8 @@ public class ContextWrapper extends Context { /** @hide */ @Override - public @Nullable String getFeatureId() { - return mBase.getFeatureId(); + public @Nullable String getAttributionTag() { + return mBase.getAttributionTag(); } @Override @@ -984,8 +984,8 @@ public class ContextWrapper extends Context { } @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 37643da375df..84b0f0e80bab 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -38,7 +38,7 @@ import java.util.ArrayList; * @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 @@ public interface IContentProvider extends IInterface { 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 @@ public interface IContentProvider extends IInterface { 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 @@ public interface IContentProvider extends IInterface { 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 @@ public interface IContentProvider extends IInterface { 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 @@ public interface IContentProvider extends IInterface { 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 @@ public interface IContentProvider extends IInterface { * 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/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index 33bd839a53f6..b4340729a220 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -123,7 +123,7 @@ public final class PermissionChecker { * @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 @@ public final class PermissionChecker { @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 @@ public final class PermissionChecker { @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 final class PermissionChecker { 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 @@ public final class PermissionChecker { * @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 @@ public final class PermissionChecker { @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 @@ public final class PermissionChecker { * @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 @@ public final class PermissionChecker { } 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 @@ public final class PermissionChecker { } 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 @@ public final class PermissionChecker { 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 @@ public final class PermissionChecker { } 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 @@ public final class PermissionChecker { 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 9f95d4d75f6f..2869abb53b37 100644 --- a/core/java/android/content/integrity/AppIntegrityManager.java +++ b/core/java/android/content/integrity/AppIntegrityManager.java @@ -25,6 +25,8 @@ import android.content.IntentSender; 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 @@ public class AppIntegrityManager { 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 4714ad7c7989..94197bb9ec17 100644 --- a/core/java/android/content/integrity/IAppIntegrityManager.aidl +++ b/core/java/android/content/integrity/IAppIntegrityManager.aidl @@ -19,6 +19,7 @@ package android.content.integrity; 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 @@ interface IAppIntegrityManager { 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 0b2b5b1f0ec4..f25ce76d9365 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1222,13 +1222,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { 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 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @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 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { gravity = source.readInt(); minWidth = source.readInt(); minHeight = source.readInt(); + windowLayoutAffinity = source.readString(); } /** @@ -1458,11 +1453,30 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { 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 a15afe04201b..c82fffa4aa00 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -25,6 +25,7 @@ import android.annotation.TestApi; 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.SparseArray; 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 @@ import java.util.UUID; * <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 class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { mHiddenApiPolicy = orig.mHiddenApiPolicy; hiddenUntilInstalled = orig.hiddenUntilInstalled; zygotePreloadName = orig.zygotePreloadName; + enableGwpAsan = orig.enableGwpAsan; } public String toString() { @@ -1703,6 +1722,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { mHiddenApiPolicy = source.readInt(); hiddenUntilInstalled = source.readInt() != 0; zygotePreloadName = source.readString(); + enableGwpAsan = sForBoolean.unparcel(source); } /** @@ -2161,6 +2182,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** {@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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { @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 5e7e0c8a2d6a..dc3a02976416 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -96,7 +96,7 @@ public class CrossProfileApps { mService.startActivityAsUser( mContext.getIApplicationThread(), mContext.getPackageName(), - mContext.getFeatureId(), + mContext.getAttributionTag(), component, targetUser.getIdentifier(), true); @@ -162,7 +162,7 @@ public class CrossProfileApps { mService.startActivityAsUserByIntent( mContext.getIApplicationThread(), mContext.getPackageName(), - mContext.getFeatureId(), + mContext.getAttributionTag(), intent, targetUser.getIdentifier(), callingActivity != null ? callingActivity.getActivityToken() : null, @@ -190,7 +190,7 @@ public class CrossProfileApps { 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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 8c3eef27dd58..0311120aebfb 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -748,4 +748,6 @@ interface IPackageManager { 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 86242fd6f82f..4e4897f59ad3 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -721,7 +721,7 @@ public class LauncherApps { } 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 @@ public class LauncherApps { @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 @@ public class LauncherApps { 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(); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index ec3590fd23cf..50bee854c027 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 @@ public class PackageInstaller { /** {@hide} */ public List<String> whitelistedRestrictedPermissions; /** {@hide} */ + public int autoRevokePermissionsMode = MODE_DEFAULT; + /** {@hide} */ public String installerPackageName; /** {@hide} */ public boolean isMultiPackage; @@ -1498,6 +1504,7 @@ public class PackageInstaller { 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 @@ public class PackageInstaller { 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 @@ public class PackageInstaller { } /** + * 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 @@ public class PackageInstaller { 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 @@ public class PackageInstaller { 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 class PackageInstaller { public String[] grantedRuntimePermissions; /** {@hide}*/ public List<String> whitelistedRestrictedPermissions; + /** {@hide}*/ + public int autoRevokePermissionsMode = MODE_DEFAULT; /** {@hide} */ public int installFlags; /** {@hide} */ @@ -2147,6 +2175,7 @@ public class PackageInstaller { referrerUri = source.readParcelable(null); grantedRuntimePermissions = source.readStringArray(); whitelistedRestrictedPermissions = source.createStringArrayList(); + autoRevokePermissionsMode = source.readInt(); installFlags = source.readInt(); isMultiPackage = source.readBoolean(); @@ -2374,6 +2403,24 @@ public class PackageInstaller { } /** + * 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()}. @@ -2660,6 +2707,7 @@ public class PackageInstaller { 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 7600a08a256c..05ff830aeed1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -557,7 +557,6 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 abstract class PackageManager { 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 @@ public abstract class PackageManager { } /** + * 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 @@ -7834,6 +7881,15 @@ public abstract class PackageManager { } /** + * @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 c6875a4b3443..18f13431c09a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -55,7 +55,6 @@ import android.compat.annotation.UnsupportedAppUsage; 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 class PackageParser { 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 class PackageParser { 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 @@ public class PackageParser { } } + resolveWindowLayout(a); + if (!setExported) { a.info.exported = a.intents.size() > 0; } @@ -4726,6 +4729,35 @@ public class PackageParser { 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 c77a267958f5..a3730672b988 100644 --- a/core/java/android/content/pm/ProcessInfo.java +++ b/core/java/android/content/pm/ProcessInfo.java @@ -23,66 +23,157 @@ import android.os.Parcelable; 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]; - } - }; - - 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())); - } - } + @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) { ... } + + 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/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index aa93d80fbfeb..1304ba8ef3a0 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.FeatureGroupInfo; 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 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage addProvider(ParsedProvider parsedProvider); - ParsingPackage addFeature(ParsedFeature permission); + ParsingPackage addAttribution(ParsedAttribution attribution); ParsingPackage addReceiver(ParsedActivity parsedReceiver); @@ -191,7 +191,11 @@ public interface ParsingPackage extends ParsingPackageRead { 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 @@ public interface ParsingPackage extends ParsingPackageRead { 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 a9b72d041f8b..3390f1616134 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.FeatureGroupInfo; 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @NonNull @Override - public List<ParsedFeature> getFeatures() { - return features; + public List<ParsedAttribution> getAttributions() { + return attributions; } @NonNull @@ -1965,6 +1978,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + @Nullable + public Boolean isGwpAsanEnabled() { + return enableGwpAsan; + } + + @Override public boolean isPartiallyDirectBootAware() { return partiallyDirectBootAware; } @@ -2010,6 +2029,16 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + public boolean isDontAutoRevokePermmissions() { + return dontAutoRevokePermissions; + } + + @Override + public boolean isAllowDontAutoRevokePermmissions() { + return allowDontAutoRevokePermissions; + } + + @Override public boolean hasPreserveLegacyExternalStorage() { return preserveLegacyExternalStorage; } @@ -2420,6 +2449,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @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 048b924e10cc..9c13c85a041e 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.PackageInfo; 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 @@ public interface ParsingPackageRead extends Parcelable { List<ConfigurationInfo> getConfigPreferences(); @NonNull - List<ParsedFeature> getFeatures(); + List<ParsedAttribution> getAttributions(); /** * @see PackageInfo#featureGroups @@ -771,6 +771,12 @@ public interface ParsingPackageRead extends Parcelable { /** @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 @@ public interface ParsingPackageRead extends Parcelable { @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 b4f21593165f..e41ed85d2438 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.ConfigurationInfo; 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.Signature; 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 @@ public class ParsingPackageUtils { ); } - 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 @@ public class ParsingPackageUtils { 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 @@ public class ParsingPackageUtils { 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 @@ public class ParsingPackageUtils { && !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 @@ public class ParsingPackageUtils { .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 d32171dfe962..4c93d0950388 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 @@ public class ParsedActivity extends ParsedMainComponent { 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 1dcf262d46ba..6e5c51a22025 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 android.view.WindowManager; 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 @@ public class ParsedActivityUtils { } } + 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 @@ public class ParsedActivityUtils { 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/ParsedFeature.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java index b8a9098cfa0e..02b3c7d9c909 100644 --- a/core/java/android/content/pm/parsing/component/ParsedFeature.java +++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java @@ -29,65 +29,65 @@ import java.util.ArrayList; import java.util.List; /** - * A {@link android.R.styleable#AndroidManifestFeature <feature>} tag parsed from the + * A {@link android.R.styleable#AndroidManifestAttribution <attribution>} 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; +public class ParsedAttribution implements Parcelable { + /** Maximum length of attribution tag */ + public static final int MAX_ATTRIBUTION_TAG_LEN = 50; - /** Maximum amount of features per package */ - private static final int MAX_NUM_FEATURES = 1000; + /** Maximum amount of attributions per package */ + private static final int MAX_NUM_ATTRIBUTIONS = 1000; - /** Id of the feature */ - public final @NonNull String id; + /** Tag of the attribution */ + public final @NonNull String tag; - /** User visible label fo the feature */ + /** User visible label fo the attribution */ public final @StringRes int label; - /** Ids of previously declared features this feature inherits from */ + /** Ids of previously declared attributions this attribution inherits from */ public final @NonNull List<String> inheritFrom; /** - * @return Is this set of features a valid combination for a single package? + * @return Is this set of attributions a valid combination for a single package? */ - public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) { - if (features == null) { + public static boolean isCombinationValid(@Nullable List<ParsedAttribution> attributions) { + if (attributions == null) { return true; } - ArraySet<String> featureIds = new ArraySet<>(features.size()); - ArraySet<String> inheritFromFeatureIds = new ArraySet<>(); + ArraySet<String> attributionTags = new ArraySet<>(attributions.size()); + ArraySet<String> inheritFromAttributionTags = new ArraySet<>(); - int numFeatures = features.size(); - if (numFeatures > MAX_NUM_FEATURES) { + int numAttributions = attributions.size(); + if (numAttributions > MAX_NUM_ATTRIBUTIONS) { return false; } - for (int featureNum = 0; featureNum < numFeatures; featureNum++) { - boolean wasAdded = featureIds.add(features.get(featureNum).id); + 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 featureNum = 0; featureNum < numFeatures; featureNum++) { - ParsedFeature feature = features.get(featureNum); + 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 (featureIds.contains(inheritFrom)) { - // Cannot inherit from a feature that is still defined + if (attributionTags.contains(inheritFrom)) { + // Cannot inherit from a attribution that is still defined return false; } - boolean wasAdded = inheritFromFeatureIds.add(inheritFrom); + boolean wasAdded = inheritFromAttributionTags.add(inheritFrom); if (!wasAdded) { // inheritFrom is not unique return false; @@ -106,7 +106,7 @@ public class ParsedFeature implements Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java + // $ 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 @@ -114,8 +114,8 @@ public class ParsedFeature implements Parcelable { @android.annotation.IntDef(prefix = "MAX_", value = { - MAX_FEATURE_ID_LEN, - MAX_NUM_FEATURES + MAX_ATTRIBUTION_TAG_LEN, + MAX_NUM_ATTRIBUTIONS }) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -124,32 +124,32 @@ public class ParsedFeature implements Parcelable { @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"; + 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 ParsedFeature. + * Creates a new ParsedAttribution. * - * @param id - * Id of the feature + * @param tag + * Tag of the attribution * @param label - * User visible label fo the feature + * User visible label fo the attribution * @param inheritFrom - * Ids of previously declared features this feature inherits from + * Ids of previously declared attributions this attribution inherits from */ @DataClass.Generated.Member - public ParsedFeature( - @NonNull String id, + public ParsedAttribution( + @NonNull String tag, @StringRes int label, @NonNull List<String> inheritFrom) { - this.id = id; + this.tag = tag; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, id); + NonNull.class, null, tag); this.label = label; com.android.internal.util.AnnotationValidations.validate( StringRes.class, null, label); @@ -166,7 +166,7 @@ public class ParsedFeature implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - dest.writeString(id); + dest.writeString(tag); dest.writeInt(label); dest.writeStringList(inheritFrom); } @@ -178,18 +178,18 @@ public class ParsedFeature implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - protected ParsedFeature(@NonNull Parcel in) { + protected ParsedAttribution(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - String _id = in.readString(); + String _tag = in.readString(); int _label = in.readInt(); List<String> _inheritFrom = new ArrayList<>(); in.readStringList(_inheritFrom); - this.id = _id; + this.tag = _tag; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, id); + NonNull.class, null, tag); this.label = _label; com.android.internal.util.AnnotationValidations.validate( StringRes.class, null, label); @@ -201,24 +201,24 @@ public class ParsedFeature implements Parcelable { } @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR - = new Parcelable.Creator<ParsedFeature>() { + public static final @NonNull Parcelable.Creator<ParsedAttribution> CREATOR + = new Parcelable.Creator<ParsedAttribution>() { @Override - public ParsedFeature[] newArray(int size) { - return new ParsedFeature[size]; + public ParsedAttribution[] newArray(int size) { + return new ParsedAttribution[size]; } @Override - public ParsedFeature createFromParcel(@NonNull Parcel in) { - return new ParsedFeature(in); + public ParsedAttribution createFromParcel(@NonNull Parcel in) { + return new ParsedAttribution(in); } }; @DataClass.Generated( - time = 1581379861853L, + time = 1583436566499L, 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)") + 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() {} diff --git a/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java b/core/java/android/content/pm/parsing/component/ParsedAttributionUtils.java index fb5280175d1a..c4b1a0eb90a0 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.Collections; 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 @@ public class ParsedFeatureUtils { 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 @@ public class ParsedFeatureUtils { sa.recycle(); } } else { - return input.error("Bad element under <feature>: " + tagName); + return input.error("Bad element under <attribution>: " + tagName); } } @@ -104,6 +111,6 @@ public class ParsedFeatureUtils { ((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/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java index da7bf984aa7f..3b6020a72469 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 @@ public class ParsedProcess implements Parcelable { @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class) protected Set<String> deniedPermissions = emptySet(); + @Nullable + protected Boolean enableGwpAsan = null; + public ParsedProcess() { } @@ -71,13 +74,15 @@ public class ParsedProcess implements Parcelable { @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 @@ public class ParsedProcess implements Parcelable { } @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 @@ public class ParsedProcess implements Parcelable { // 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 @@ public class ParsedProcess implements Parcelable { // 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 @@ public class ParsedProcess implements Parcelable { 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 @@ public class ParsedProcess implements Parcelable { }; @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 48250666a58b..3820790cc2b1 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 @@ public class ParsedProcessUtils { 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/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl index 61310f302fe4..8bcaf828be97 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl @@ -35,7 +35,7 @@ oneway interface IBiometricServiceReceiverInternal { // 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 b3a1ee2f9b69..7e72b73db358 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2884,12 +2884,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * 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 cc0c1a309038..30ee32604939 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -681,7 +681,7 @@ public abstract class CameraDevice implements AutoCloseable { * </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 a091f84fe463..e81c649796a0 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -160,8 +160,8 @@ public final class CameraManager { * @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 @@ public final class CameraManager { * * @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 @@ public final class CameraManager { "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 @@ public final class CameraManager { try { ConcurrentCameraIdCombination[] cameraIdCombinations = - cameraService.getConcurrentStreamingCameraIds(); + cameraService.getConcurrentCameraIds(); for (ConcurrentCameraIdCombination comb : cameraIdCombinations) { mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination()); } @@ -1372,7 +1372,7 @@ public final class CameraManager { 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { } } 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 @@ public final class CameraManager { /** * 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/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index f0fab6a99d14..20d9c30bb4cc 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -721,7 +721,7 @@ public final class MandatoryStreamCombination { /** * 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/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java index d009144034f0..1710ccb31973 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -368,7 +368,14 @@ public class ConnectivityDiagnosticsManager { /** 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 */ diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index fc6954fb4808..81735ac8f693 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -3222,7 +3222,9 @@ public class ConnectivityManager { /** {@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 @@ public class ConnectivityManager { /** {@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 @@ public class ConnectivityManager { * @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 @@ public class ConnectivityManager { * @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 @@ public class ConnectivityManager { /** @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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java index 2b8b7e69dec9..6c0ba2f63a80 100644 --- a/core/java/android/net/KeepalivePacketData.java +++ b/core/java/android/net/KeepalivePacketData.java @@ -22,7 +22,6 @@ import static android.net.InvalidPacketException.ERROR_INVALID_PORT; 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 @@ import java.net.InetAddress; /** * Represents the actual packets that are sent by the * {@link android.net.SocketKeepalive} API. - * * @hide */ @SystemApi @@ -54,6 +52,9 @@ public class KeepalivePacketData { /** 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 @@ public class KeepalivePacketData { 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/Network.java b/core/java/android/net/Network.java index c5681cb6d7e4..6f5471baa7ab 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -61,6 +61,7 @@ import javax.net.SocketFactory; 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 fef353f604dc..5c754a1b9733 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -78,6 +78,7 @@ public abstract class NetworkAgent { /** * 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 @@ public abstract class NetworkAgent { * * @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 @@ public abstract class NetworkAgent { * * @deprecated this is for backward compatibility only. * @param extraInfo the ExtraInfo. + * @hide */ @Deprecated public void setLegacyExtraInfo(@Nullable final String extraInfo) { @@ -711,6 +714,7 @@ public abstract class NetworkAgent { /** * 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 7e2db4a4fa95..ca9328a713f0 100644 --- a/core/java/android/net/NetworkAgentConfig.java +++ b/core/java/android/net/NetworkAgentConfig.java @@ -108,6 +108,7 @@ public final class NetworkAgentConfig implements Parcelable { /** * * @return whether the sign in to network notification is enabled by this configuration. + * @hide */ public boolean isProvisioningNotificationEnabled() { return !provisioningNotificationDisabled; @@ -122,6 +123,7 @@ public final class NetworkAgentConfig implements Parcelable { /** * @return the subscriber ID, or null if none. + * @hide */ @Nullable public String getSubscriberId() { @@ -138,6 +140,7 @@ public final class NetworkAgentConfig implements Parcelable { /** * @return whether NAT64 prefix detection is enabled. + * @hide */ public boolean isNat64DetectionEnabled() { return !skip464xlat; @@ -247,6 +250,7 @@ public final class NetworkAgentConfig implements Parcelable { * 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 @@ public final class NetworkAgentConfig implements Parcelable { * 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 @@ public final class NetworkAgentConfig implements Parcelable { * 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 83f99802b85f..116e343ff250 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -613,7 +613,6 @@ public final class NetworkCapabilities implements Parcelable { * @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 14442a2088cd..1922b6df2e7f 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -21,7 +21,6 @@ import static android.content.pm.PackageManager.GET_SIGNATURES; 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 @@ import java.util.concurrent.ConcurrentHashMap; * @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 @@ public class NetworkPolicyManager { /** * 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 @@ public class NetworkPolicyManager { /** @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 @@ public class NetworkPolicyManager { /** @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 @@ public class NetworkPolicyManager { * 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 @@ public class NetworkPolicyManager { * @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 @@ public class NetworkPolicyManager { * * @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 @@ public class NetworkPolicyManager { } /** @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 2c0e4aa700b1..418d6915d4b3 100644 --- a/core/java/android/net/NetworkProvider.java +++ b/core/java/android/net/NetworkProvider.java @@ -106,10 +106,12 @@ public class NetworkProvider { } // 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/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 9ca1c3313670..fab906b9e122 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -157,20 +157,20 @@ public final class BugreportManager { 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 @@ public final class BugreportManager { 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 @@ public final class BugreportManager { @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 9c999b202797..590fbb36a0e8 100644 --- a/core/java/android/os/ConfigUpdate.java +++ b/core/java/android/os/ConfigUpdate.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; /** @@ -114,20 +115,36 @@ public final class ConfigUpdate { = "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) 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 f2fb5b246f39..09ccb7238b34 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -23,6 +23,7 @@ import android.annotation.TestApi; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; +import android.compat.Compatibility; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -88,6 +89,16 @@ public class Environment { private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT, "/apex"); + /** + * See definition in com.android.providers.media.LocalCallingIdentity + */ + private static final long DEFAULT_SCOPED_STORAGE = 149924527L; + + /** + * See definition in com.android.providers.media.LocalCallingIdentity + */ + private static final long FORCE_ENABLE_SCOPED_STORAGE = 132649864L; + @UnsupportedAppUsage private static UserEnvironment sCurrentUser; private static boolean sUserRequired; @@ -1191,12 +1202,13 @@ public class Environment { } /** - * 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 +1219,19 @@ public class Environment { } /** - * 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 +1245,23 @@ public class Environment { return false; } - if (packageManager.checkPermission(Manifest.permission.WRITE_MEDIA_STORAGE, - context.getPackageName()) == PackageManager.PERMISSION_GRANTED) { - return true; + // 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 (packageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES, - context.getPackageName()) == PackageManager.PERMISSION_GRANTED) { + // 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; } - 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; - } - } + final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED; } @@ -1298,6 +1310,16 @@ public class Environment { } } + 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 b478dbe2555d..a7e326378228 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -77,6 +77,7 @@ public final class Looper { @UnsupportedAppUsage final MessageQueue mQueue; final Thread mThread; + private boolean mInLoop; @UnsupportedAppUsage private Printer mLogging; @@ -155,6 +156,12 @@ public final class Looper { 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 d7af1b9faa97..a557bd994c13 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -607,6 +607,7 @@ public class Process { * 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 @@ public class Process { @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 @@ public class Process { 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); } /** diff --git a/core/java/android/os/Users.md b/core/java/android/os/Users.md new file mode 100644 index 000000000000..3bbbe5452fd3 --- /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 2d218f4f2541..1992f1dc911e 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -963,7 +963,7 @@ public abstract class VibrationEffect implements Parcelable { PRIMITIVE_QUICK_RISE, PRIMITIVE_SLOW_RISE, PRIMITIVE_QUICK_FALL, - PRIMITIVE_LIGHT_TICK, + PRIMITIVE_TICK, }) @Retention(RetentionPolicy.SOURCE) public @interface Primitive {} @@ -981,10 +981,14 @@ public abstract class VibrationEffect implements Parcelable { * 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 @@ public abstract class VibrationEffect implements Parcelable { * 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 @@ public abstract class VibrationEffect implements Parcelable { * */ 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 @@ public abstract class VibrationEffect implements Parcelable { 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 34cec061edcd..5f3f14facd75 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -333,6 +333,7 @@ public class ZygoteProcess { * 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 @@ public class ZygoteProcess { @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 @@ public class ZygoteProcess { 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 @@ public class ZygoteProcess { * @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 @@ public class ZygoteProcess { @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 @@ public class ZygoteProcess { 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 @@ public class ZygoteProcess { 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 182732cebdf1..8faf158b72a0 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 @@ parcelable IncrementalNewFileParams { 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 bf31bc206278..7092751c0d7e 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -20,8 +20,6 @@ import android.annotation.NonNull; 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 @@ public final class IncrementalStorage { 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 @@ public final class IncrementalStorage { 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 @@ public final class IncrementalStorage { + " 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 (hashingInfo.log2BlockSize != V4Signature.LOG2_BLOCK_SIZE_4096_BYTES) { + throw new IOException("Unsupported log2BlockSize: " + hashingInfo.log2BlockSize); } - if (additionalData.length > INCFS_MAX_ADD_DATA_SIZE) { + 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 6d334f539fc9..71f931da1a92 100644 --- a/core/java/android/os/incremental/V4Signature.java +++ b/core/java/android/os/incremental/V4Signature.java @@ -20,9 +20,12 @@ import android.os.ParcelFileDescriptor; 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 @@ import java.io.IOException; */ 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 @@ public class V4Signature { * 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() { - return this.version == SUPPORTED_VERSION; + /** + * 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(); } - 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); + public boolean isVersionSupported() { + return this.version == SUPPORTED_VERSION; } - 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 a2def7fff2d2..f43a2523b5b3 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -100,10 +100,11 @@ public abstract class StorageManagerInternal { } /** - * 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. diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 0483514e6297..f01139542541 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -42,6 +42,6 @@ oneway interface IPermissionController { 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 2615c98a6d5e..09df72c286d0 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -106,4 +106,12 @@ interface IPermissionManager { 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 2a1857fd0027..f08e3d25632b 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -46,6 +46,7 @@ import android.content.pm.ResolveInfo; 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 @@ public final class PermissionControllerManager { } /** - * @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 263b2c7a4ac7..4a42230ad15a 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -218,11 +218,14 @@ public abstract class PermissionControllerService extends Service { * 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 @@ public abstract class PermissionControllerService extends Service { } @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 0bd211d70e89..8308bb39d8c5 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -40,11 +40,13 @@ import android.os.UserHandle; 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 @@ public final class PermissionManager { } } + /** + * 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 000000000000..e62bd0a88bf6 --- /dev/null +++ b/core/java/android/permission/Permissions.md @@ -0,0 +1,832 @@ +<!-- + 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. + +#### 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. + +## 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/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index aa511cc46de9..fb81d675939c 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -399,6 +399,13 @@ public final class DeviceConfig { 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 a80153d691f0..327bca268a7b 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -1081,7 +1081,7 @@ public abstract class DocumentsProvider extends ContentProvider { // 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { && 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { } } 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 23c074c88f01..d8679b20e6dc 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2624,7 +2624,7 @@ public final class Settings { 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); @@ -2644,7 +2644,7 @@ public final class Settings { 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); @@ -2719,14 +2719,14 @@ public final class Settings { 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) { @@ -2796,13 +2796,13 @@ public final class Settings { 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) { @@ -2895,7 +2895,7 @@ public final class Settings { } // 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 @@ -5540,7 +5540,7 @@ public final class Settings { } 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); @@ -6090,10 +6090,7 @@ public final class Settings { * 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"; /** @@ -8500,7 +8497,6 @@ public final class Settings { * * @hide */ - @SystemApi public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled"; /** @@ -13386,7 +13382,7 @@ public final class Settings { } 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); @@ -14361,7 +14357,7 @@ public final class Settings { 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); @@ -14390,7 +14386,7 @@ public final class Settings { 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 03b38ab9b3ee..e7b360da47b8 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3551,7 +3551,6 @@ public final class Telephony { * can manage DPC-owned APNs. * @hide */ - @SystemApi public static final @NonNull Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc"); /** @@ -3864,7 +3863,6 @@ public final class Telephony { * Integer value denoting an invalid APN id * @hide */ - @SystemApi public static final int INVALID_APN_ID = -1; /** diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 886b433d4ade..08aa534be152 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -232,22 +232,6 @@ public final class Dataset implements Parcelable { * 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 @@ public final class Dataset implements Parcelable { } /** + * 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 @@ public final class Dataset implements Parcelable { */ @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 @@ public final class Dataset implements Parcelable { 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/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl index 101165176293..1bcc76bfca44 100644 --- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl +++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl @@ -25,7 +25,8 @@ import android.view.SurfaceControlViewHost; * @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 ee15283715ff..f0a72c501f77 100644 --- a/core/java/android/service/autofill/InlineSuggestionRenderService.java +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -94,15 +94,24 @@ public abstract class InlineSuggestionRenderService extends Service { final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(), hostInputToken); - host.addView(suggestionRoot, lp); + host.setView(suggestionRoot, lp); suggestionRoot.setOnClickListener((v) -> { try { - callback.onAutofill(); + callback.onClick(); } catch (RemoteException e) { - Log.w(TAG, "RemoteException calling onAutofill()"); + Log.w(TAG, "RemoteException calling onClick()"); } }); + suggestionRoot.setOnLongClickListener((v) -> { + try { + 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/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index ed27dd568823..5b08ae20f071 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -488,7 +488,8 @@ public abstract class AugmentedAutofillService extends Service { 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/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java index d2494a544d7a..ae67068d9f32 100644 --- a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java @@ -24,8 +24,6 @@ import android.os.RemoteException; 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 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback { * 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 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback { } private void onSuccessInternal(GetWalletCardsResponse response) { - Log.i(TAG, "onSuccessInternal"); if (mCalled) { Log.w(TAG, "already called"); return; @@ -86,7 +82,6 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback { 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 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback { } 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 be9ab11fb3a2..4f5b13981d64 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 @@ public interface QuickAccessWalletClient { @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 @@ public interface QuickAccessWalletClient { 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 37a8703ddebe..e4dd082edaa5 100644 --- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java @@ -18,6 +18,7 @@ package android.service.quickaccesswallet; 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 android.util.Log; 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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser @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 class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser 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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null)); } }); + } @Override public void selectWalletCard(@NonNull SelectWalletCardRequest request) { - Log.i(TAG, "selectWalletCard"); if (!isWalletServiceAvailable()) { return; } @@ -153,7 +162,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser if (!isWalletServiceAvailable()) { return; } - Log.i(TAG, "notifyWalletDismissed"); executeApiCall(new ApiCaller("onWalletDismissed") { @Override public void performApiCall(IQuickAccessWalletService service) throws RemoteException { @@ -164,15 +172,20 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser @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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser if (!isWalletServiceAvailable()) { return; } - Log.i(TAG, "unregisterWalletServiceEventListener"); executeApiCall(new ApiCaller("unregisterListener") { @Override public void performApiCall(IQuickAccessWalletService service) throws RemoteException { @@ -209,8 +221,12 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser } @Override + public void close() throws IOException { + disconnect(); + } + + @Override public void disconnect() { - Log.i(TAG, "disconnect"); mHandler.post(() -> disconnectInternal(true)); } @@ -241,18 +257,15 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser } 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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser } 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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser * 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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser } 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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser } 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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser 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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser @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 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser @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 23173a820a2e..31e51bb945d6 100644 --- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java @@ -66,13 +66,11 @@ class QuickAccessWalletServiceInfo { 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 e6ae0abef918..b09d2e9e7f13 100644 --- a/core/java/android/service/quickaccesswallet/WalletCard.java +++ b/core/java/android/service/quickaccesswallet/WalletCard.java @@ -180,7 +180,7 @@ public final class WalletCard implements Parcelable { * 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/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 8e6f77b2fd0c..4a0dd870f797 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1314,7 +1314,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall 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 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall 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/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index e93ba16fa594..92f3538a48de 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -342,7 +342,7 @@ public class SpeechRecognizer { } 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 @@ public class SpeechRecognizer { } 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 @@ public class SpeechRecognizer { 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 class SpeechRecognizer { 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/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 7238b1244eb3..ab9df56560b3 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -116,7 +116,7 @@ public class TelephonyRegistryManager { 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 @@ public class TelephonyRegistryManager { 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/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index abd04cc2b0e7..79eb9f6e749c 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,6 +213,12 @@ public class ApkSignatureSchemeV3Verifier { verityDigest, apk.length(), signatureInfo); } + if (contentDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA512)) { + result.digest = contentDigests.get(CONTENT_DIGEST_CHUNKED_SHA512); + } else if (contentDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA256)) { + result.digest = contentDigests.get(CONTENT_DIGEST_CHUNKED_SHA256); + } + return result; } @@ -568,6 +576,7 @@ public class ApkSignatureSchemeV3Verifier { 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 b6b8089b1743..8c240d99f590 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 @@ import sun.security.pkcs.ParsingException; * @hide for internal use only. */ public class ApkSignatureSchemeV4Verifier { - /** - * Extracts APK Signature Scheme v4 signatures of the provided APK and returns the certificates - * associated with each signer. + * Extracts and verifies APK Signature Scheme v4 signatures of the provided APK and returns the + * certificates associated with each signer. */ - public static Certificate[] extractCertificates(String apkFile) + public static VerifiedSigner 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."); + 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 { - PKCS7 pkcs7 = new PKCS7(rawSignature); - return pkcs7.getCertificates(); - } catch (ParsingException e) { - throw new SecurityException("Failed to parse signature and extract certificates", e); + 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); + } + + /** + * Verified APK Signature Scheme v4 signer, including V3 digest. + * + * @hide for internal use only. + */ + public static class VerifiedSigner { + public final Certificate[] certs; + public byte[] v3Digest; + + public VerifiedSigner(Certificate[] certs, byte[] v3Digest) { + this.certs = certs; + this.v3Digest = v3Digest; + } + } } diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index f325c2171c23..c1cee48cc663 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -168,7 +168,7 @@ public class ApkSignatureVerifier { /** * 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 @@ public class ApkSignatureVerifier { 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 759c8649532b..a7ae32d1baa2 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.isSupportedSignatureAlgorith 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.NoSuchAlgorithmException; 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 @@ import java.util.zip.ZipEntry; */ 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 @@ public abstract class SourceStampVerifier { /** 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 @@ public abstract class SourceStampVerifier { 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 @@ public abstract class SourceStampVerifier { } 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/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index 74fac2b40472..6784cf7407fa 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -165,10 +165,16 @@ public final class ImeFocusController { 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 5d5edecead22..4227f78564a7 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 @@ public interface InsetsAnimationControlCallbacks { 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 @@ public interface InsetsAnimationControlCallbacks { /** * 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 @@ public interface InsetsAnimationControlCallbacks { * 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 ae509f3a82c4..baee4123ef47 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -31,9 +31,7 @@ import android.util.ArraySet; 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 @@ import java.util.ArrayList; * @hide */ @VisibleForTesting -public class InsetsAnimationControlImpl implements WindowInsetsAnimationController { +public class InsetsAnimationControlImpl implements WindowInsetsAnimationController, + InsetsAnimationControlRunner { private final Rect mTmpFrame = new Rect(); @@ -84,8 +83,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll 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 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll 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 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll 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 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll 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,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll } setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */); mFinished = true; + mShownOnFinish = shown; } @@ -205,19 +210,23 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return mAnimation.getFraction(); } - public void onCancelled() { + @Override + public void cancel() { if (mFinished) { return; } mCancelled = true; mListener.onCancelled(); + + releaseLeashes(); } public boolean isCancelled() { return mCancelled; } - WindowInsetsAnimation getAnimation() { + @Override + public WindowInsetsAnimation getAnimation() { return mAnimation; } @@ -225,6 +234,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll 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 000000000000..0711c3e166d8 --- /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 000000000000..cdf97333a0ca --- /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 000000000000..13b4cd83b4df --- /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 607886f3b13b..123b9db75804 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -23,6 +23,7 @@ import static android.view.WindowInsets.Type.ime; 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.Interpolator; 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 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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,6 +206,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation onAnimationFinish(); } }); + if (mUseSfVsync) { + mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get()); + } mAnimator.start(); } @@ -228,12 +244,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation */ 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 +268,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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 +276,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation this.animationType = animationType; this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation; this.cancellationSignal = cancellationSignal; + this.useInsetsAnimationThread = useInsetsAnimationThread; } final @InsetsType int types; @@ -269,6 +286,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final @AnimationType int animationType; final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation; final CancellationSignal cancellationSignal; + final boolean useInsetsAnimationThread; } private final String TAG = "InsetsControllerImpl"; @@ -347,15 +365,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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()); + } - // 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); + if (control.applyChangeInsets(state)) { + mTmpFinishedControls.add(control); + } } } @@ -494,13 +517,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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 +569,27 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } @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; + 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 +599,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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; + return; } cancelExistingControllers(types); mLastStartedAnimTypes |= types; @@ -600,29 +626,42 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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; + 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 +705,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } 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 +744,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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 */); } @@ -725,13 +764,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @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 +795,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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 +805,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } - 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 +883,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @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 +917,25 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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 +970,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @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 +984,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } 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 252fc0c82cf7..332573449e18 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -117,7 +117,7 @@ public class InsetsSourceConsumer { } } 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 75f6eab798ce..f3ec65f997ba 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -22,6 +22,8 @@ import android.os.Parcel; 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 @@ public class InsetsSourceControl implements Parcelable { 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 8ec5df85dc7b..18e0132e2c4e 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -35,6 +35,7 @@ import android.widget.RemoteViews; 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 @@ public class NotificationHeaderView extends ViewGroup { 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 @@ public class NotificationHeaderView extends ViewGroup { 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 @@ public class NotificationHeaderView extends ViewGroup { return mIconColor; } - @RemotableViewMethod - public void setOriginalNotificationColor(int color) { - mOriginalNotificationColor = color; - } - public int getOriginalNotificationColor() { - return mOriginalNotificationColor; + return mExpandButton.getOriginalNotificationColor(); } @RemotableViewMethod @@ -371,7 +366,7 @@ public class NotificationHeaderView extends ViewGroup { 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 7f3641803770..e8d9bb50c290 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 @@ public class PendingInsetsController implements WindowInsetsController { } @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 @@ public class PendingInsetsController implements WindowInsetsController { 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(); + } + } + private interface PendingRequest { void replay(InsetsController controller); } diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index a3b3f1f72310..41a384797521 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -26,6 +26,8 @@ import android.os.Parcel; 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 @@ -159,7 +161,8 @@ public class SurfaceControlViewHost { * @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 +175,18 @@ public class SurfaceControlViewHost { * @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 6236e6ea31e9..708a09467247 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3318,7 +3318,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ @TestApi - public void setAutofilled(boolean isAutofilled) { + public void setAutofilled(boolean isAutofilled, boolean hideHighlight) { boolean wasChanged = isAutofilled != isAutofilled(); if (wasChanged) { @@ -11751,6 +11773,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags3 &= ~PFLAG3_IS_AUTOFILLED; } + if (hideHighlight) { + mPrivateFlags4 |= PFLAG4_AUTOFILL_HIDE_HIGHLIGHT; + } else { + mPrivateFlags4 &= ~PFLAG4_AUTOFILL_HIDE_HIGHLIGHT; + } + invalidate(); } } @@ -20569,6 +20597,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, state.mStartActivityRequestWhoSaved = mStartActivityRequestWho; state.mIsAutofilled = isAutofilled(); + state.mHideHighlight = hideAutofillHighlight(); state.mAutofillViewId = mAutofillViewId; return state; } @@ -20645,7 +20674,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mSavedData; String mStartActivityRequestWhoSaved; boolean mIsAutofilled; + boolean mHideHighlight; int mAutofillViewId; /** @@ -28549,6 +28580,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mSavedData = source.readInt(); mStartActivityRequestWhoSaved = source.readString(); mIsAutofilled = source.readBoolean(); + mHideHighlight = source.readBoolean(); mAutofillViewId = source.readInt(); } @@ -28568,6 +28600,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, out.writeInt(mSavedData); out.writeString(mStartActivityRequestWhoSaved); out.writeBoolean(mIsAutofilled); + out.writeBoolean(mHideHighlight); out.writeInt(mAutofillViewId); } @@ -29066,8 +29099,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 7d4ec3dcf2e7..e34e84c977ea 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -7258,6 +7258,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager : 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 26ac4fc4ddc2..dd34bcb018b9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -81,6 +81,7 @@ import android.graphics.Canvas; 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 @@ public final class ViewRootImpl implements ViewParent, insets = insets.consumeDisplayCutout(); } host.dispatchApplyWindowInsets(insets); + mAttachInfo.delayNotifyContentCaptureInsetsEvent(insets.getInsets(Type.all())); Trace.traceEnd(Trace.TRACE_TAG_VIEW); } @@ -3118,6 +3120,8 @@ public final class ViewRootImpl implements ViewParent, 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); } diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java index 701bd3158b1e..faaf9203af32 100644 --- a/core/java/android/view/WindowInsetsAnimationControlListener.java +++ b/core/java/android/view/WindowInsetsAnimationControlListener.java @@ -16,7 +16,6 @@ package android.view; -import android.annotation.Hide; import android.annotation.NonNull; import android.view.WindowInsets.Type.InsetsType; import android.view.inputmethod.EditorInfo; diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index 2ad557e6d9f6..0282ecac8920 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -156,16 +156,17 @@ public interface WindowInsetsController { * 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/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 01a1c77d50af..410d9afe73da 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -504,6 +504,7 @@ public final class WindowManagerGlobal { } void doRemoveView(ViewRootImpl root) { + boolean allViewsRemoved; synchronized (mLock) { final int index = mRoots.indexOf(root); if (index >= 0) { @@ -512,10 +513,17 @@ public final class WindowManagerGlobal { 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/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index dda4e8b63a91..39a9ed4a82e7 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1236,7 +1236,7 @@ public final class AutofillManager { // 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)) { @@ -1244,13 +1244,13 @@ public final class AutofillManager { 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); } } @@ -2166,7 +2166,8 @@ public final class AutofillManager { * @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) { @@ -2175,11 +2176,12 @@ public final class AutofillManager { } 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; @@ -2238,7 +2240,7 @@ public final class AutofillManager { // synchronously. // If autofill happens async, the view is set to autofilled in // notifyValueChanged. - setAutofilledIfValuesIs(view, value); + setAutofilledIfValuesIs(view, value, hideHighlight); numApplied++; } @@ -3256,10 +3258,11 @@ public final class AutofillManager { } @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)); } } @@ -3397,10 +3400,11 @@ public final class AutofillManager { } @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 03054dfdc7ea..8526c1e443c8 100644 --- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl +++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl @@ -38,7 +38,8 @@ interface IAugmentedAutofillManagerClient { /** * 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 3903665f2cde..4371b3c54f94 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -44,7 +44,8 @@ oneway interface IAutoFillManagerClient { /** * 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 7487ec4d921c..44b4353871a2 100644 --- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -17,6 +17,7 @@ package android.view.contentcapture; 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 @@ final class ChildContentCaptureSession extends ContentCaptureSession { } @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 c29d251e6535..ea34d948c91a 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; 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 final class ContentCaptureEvent implements Parcelable { */ 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 @@ public final class ContentCaptureEvent implements Parcelable { 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 @@ public final class ContentCaptureEvent implements Parcelable { 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 @@ public final class ContentCaptureEvent implements Parcelable { return this; } + /** @hide */ + @NonNull + public ContentCaptureEvent setInsets(@NonNull Insets insets) { + mInsets = insets; + return this; + } + /** * Gets the type of the event. * @@ -305,6 +320,16 @@ public final class ContentCaptureEvent implements Parcelable { } /** + * 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 @@ public final class ContentCaptureEvent implements Parcelable { } if (mClientContext != null) { pw.print(", context="); mClientContext.dump(pw); pw.println(); - + } + if (mInsets != null) { + pw.print(", insets="); pw.println(mInsets); } } @@ -401,6 +428,9 @@ public final class ContentCaptureEvent implements Parcelable { if (mClientContext != null) { string.append(", context=").append(mClientContext); } + if (mInsets != null) { + string.append(", insets=").append(mInsets); + } return string.append(']').toString(); } @@ -424,6 +454,9 @@ public final class ContentCaptureEvent implements Parcelable { 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 @@ public final class ContentCaptureEvent implements Parcelable { 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 @@ public final class ContentCaptureEvent implements Parcelable { 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 2134dab7986b..012f5e6d3507 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -23,6 +23,7 @@ import android.annotation.IntDef; 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 @@ public abstract class ContentCaptureSession implements AutoCloseable { 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 96f224fef251..893d38dcfde7 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_RESUM 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.annotation.UiThread; 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 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } @Override + void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) { + notifyViewInsetsChanged(mId, viewInsets); + } + + @Override public void internalNotifyViewTreeEvent(boolean started) { notifyViewTreeEvent(mId, started); } @@ -642,6 +649,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } /** 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/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java index 650061396992..dd1738a5ff29 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 @@ public final class InlineSuggestion implements Parcelable { } - /** * 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 @@ public final class InlineSuggestion implements Parcelable { } @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 @@ public final class InlineSuggestion implements Parcelable { 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 @@ public final class InlineSuggestion implements Parcelable { 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 @@ public final class InlineSuggestion implements Parcelable { - // 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 @@ public final class InlineSuggestion implements Parcelable { }; @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/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index 6246b507ba59..842ba2975b3b 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -21,15 +21,12 @@ import android.annotation.IntRange; 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 @@ public final class ConversationActions implements Parcelable { @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 @@ public final class ConversationActions implements Parcelable { 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 @@ public final class ConversationActions implements Parcelable { maxSuggestions, hints, extras); - request.setCallingPackageName(callingPackageName); - request.setUserId(userId); - request.setUseDefaultTextClassifier(useDefaultTextClassifier); + request.setSystemTextClassifierMetadata(systemTcMetadata); return request; } @@ -368,10 +357,8 @@ public final class ConversationActions implements Parcelable { 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 @@ public final class ConversationActions implements Parcelable { } /** - * 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/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java index e0f29a981d89..9a5454472076 100644 --- a/core/java/android/view/textclassifier/SelectionEvent.java +++ b/core/java/android/view/textclassifier/SelectionEvent.java @@ -19,10 +19,8 @@ package android.view.textclassifier; 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 @@ public final class SelectionEvent implements Parcelable { 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 @@ public final class SelectionEvent implements Parcelable { private int mEnd; private int mSmartStart; private int mSmartEnd; - private boolean mUseDefaultTextClassifier; + @Nullable private SystemTextClassifierMetadata mSystemTcMetadata; SelectionEvent( int start, int end, @@ -161,6 +158,7 @@ public final class SelectionEvent implements Parcelable { mEventType = in.readInt(); mEntityType = in.readString(); mWidgetVersion = in.readInt() > 0 ? in.readString() : null; + // TODO: remove mPackageName once aiai does not need it mPackageName = in.readString(); mWidgetType = in.readString(); mInvocationMethod = in.readInt(); @@ -175,8 +173,7 @@ public final class SelectionEvent implements Parcelable { mEnd = in.readInt(); mSmartStart = in.readInt(); mSmartEnd = in.readInt(); - mUserId = in.readInt(); - mUseDefaultTextClassifier = in.readBoolean(); + mSystemTcMetadata = in.readParcelable(null); } @Override @@ -189,6 +186,7 @@ public final class SelectionEvent implements Parcelable { if (mWidgetVersion != null) { dest.writeString(mWidgetVersion); } + // TODO: remove mPackageName once aiai does not need it dest.writeString(mPackageName); dest.writeString(mWidgetType); dest.writeInt(mInvocationMethod); @@ -205,8 +203,7 @@ public final class SelectionEvent implements Parcelable { dest.writeInt(mEnd); dest.writeInt(mSmartStart); dest.writeInt(mSmartEnd); - dest.writeInt(mUserId); - dest.writeBoolean(mUseDefaultTextClassifier); + dest.writeParcelable(mSystemTcMetadata, flags); } @Override @@ -409,45 +406,26 @@ public final class SelectionEvent implements Parcelable { */ @NonNull public String getPackageName() { - return mPackageName; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : ""; } /** - * 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 +454,7 @@ public final class SelectionEvent implements Parcelable { mPackageName = context.getPackageName(); mWidgetType = context.getWidgetType(); mWidgetVersion = context.getWidgetVersion(); - mUserId = context.getUserId(); + mSystemTcMetadata = context.getSystemTextClassifierMetadata(); } /** @@ -663,10 +641,9 @@ public final class SelectionEvent implements Parcelable { @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 +662,6 @@ public final class SelectionEvent implements Parcelable { && 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 +674,7 @@ public final class SelectionEvent implements Parcelable { && mEnd == other.mEnd && mSmartStart == other.mSmartStart && mSmartEnd == other.mSmartEnd - && mUseDefaultTextClassifier == other.mUseDefaultTextClassifier; + && mSystemTcMetadata == other.mSystemTcMetadata; } @Override @@ -706,15 +682,14 @@ public final class SelectionEvent implements Parcelable { 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/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java index fe5e8d658dc3..86ef4e103990 100644 --- a/core/java/android/view/textclassifier/SystemTextClassifier.java +++ b/core/java/android/view/textclassifier/SystemTextClassifier.java @@ -18,7 +18,6 @@ package android.view.textclassifier; 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,7 +38,8 @@ import java.util.concurrent.CountDownLatch; 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) @@ -50,14 +50,19 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { } 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 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { 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 @@ public final class SystemTextClassifier implements TextClassifier { @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 index 729e8e5556a8..4d4e90a4f346 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 000000000000..971e3e25c975 --- /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 00f762b44a07..8b9d12916595 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -21,7 +21,6 @@ import android.annotation.IntDef; 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.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.View.OnClickListener; @@ -552,10 +550,7 @@ public final class TextClassification implements Parcelable { @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 +611,33 @@ public final class TextClassification implements Parcelable { } /** - * 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; - } - - /** - * 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; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null; } /** - * 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 +739,8 @@ public final class TextClassification implements Parcelable { 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 +751,12 @@ public final class TextClassification implements Parcelable { 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/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java index d58d175c9c93..f2323c625d15 100644 --- a/core/java/android/view/textclassifier/TextClassificationContext.java +++ b/core/java/android/view/textclassifier/TextClassificationContext.java @@ -18,10 +18,8 @@ package android.view.textclassifier; 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,11 @@ import java.util.Objects; */ public final class TextClassificationContext implements Parcelable { - private final String mPackageName; + // NOTE: Modify packageName only in the constructor or in setSystemTextClassifierMetadata() + 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, @@ -58,42 +55,26 @@ public final class TextClassificationContext implements Parcelable { } /** - * 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. * + * <p><b>NOTE: </b>This will override the value returned in {@link getPackageName()}. * @hide */ - void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { - mUseDefaultTextClassifier = useDefaultTextClassifier; + void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) { + mSystemTcMetadata = systemTcMetadata; + if (mSystemTcMetadata != null) { + mPackageName = mSystemTcMetadata.getCallingPackageName(); + } } /** - * 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 +99,8 @@ public final class TextClassificationContext implements Parcelable { @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 +157,14 @@ public final class TextClassificationContext implements Parcelable { 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/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java index 58024dcc09b9..1e8253db888a 100644 --- a/core/java/android/view/textclassifier/TextLanguage.java +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -20,12 +20,10 @@ import android.annotation.FloatRange; 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 @@ public final class TextLanguage implements Parcelable { 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 @@ public final class TextLanguage implements Parcelable { } /** - * 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 @@ public final class TextLanguage implements Parcelable { @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 7430cb38b987..dea3a9010b18 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -20,13 +20,11 @@ import android.annotation.FloatRange; 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 @@ public final class TextLinks implements Parcelable { @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 @@ public final class TextLinks implements Parcelable { } /** - * 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; - } - - /** - * 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; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null; } /** - * 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 @@ public final class TextLinks implements Parcelable { 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 575a072d7066..d8a632d10bc3 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -20,12 +20,10 @@ import android.annotation.FloatRange; 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 @@ public final class TextSelection implements Parcelable { @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 @@ public final class TextSelection implements Parcelable { } /** - * 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; - } - - /** - * 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; + return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null; } /** - * 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 @@ public final class TextSelection implements Parcelable { 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 @@ public final class TextSelection implements Parcelable { 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/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 01a0e9b15463..7f6c0d2077f1 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2818,7 +2818,7 @@ public class RemoteViews implements Parcelable, Filter { /** * 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 @@ public class RemoteViews implements Parcelable, Filter { /** * 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 @@ public class RemoteViews implements Parcelable, Filter { /** * 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/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index fa567f235c94..ec2653fe67b2 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.Context; 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 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { @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 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { } 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 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { 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 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { 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()) diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java index eb746127a351..e0bbc04515e0 100644 --- a/core/java/com/android/internal/app/AbstractResolverComparator.java +++ b/core/java/com/android/internal/app/AbstractResolverComparator.java @@ -221,6 +221,12 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC */ 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 04ad7e9ee8a7..ba8c7b32ad02 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.ArrayList; 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 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator } @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 5620bff5142b..3696c83d566b 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -223,6 +223,11 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements break; case CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT: + mMinTimeoutPassed = true; unbindRemainingServices(); maybeStopServiceRequestTimer(); break; @@ -513,7 +524,7 @@ public class ChooserActivity extends ResolverActivity implements if (adapterForUserHandle != null) { adapterForUserHandle.addServiceResults( resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1, - mDirectShareShortcutInfoCache); + mDirectShareShortcutInfoCache, mServiceConnections); } } break; @@ -1481,7 +1492,7 @@ public class ChooserActivity extends ResolverActivity implements /* origTarget */ null, Lists.newArrayList(mCallerChooserTargets), TARGET_TYPE_DEFAULT, - /* directShareShortcutInfoCache */ null); + /* directShareShortcutInfoCache */ null, mServiceConnections); } } @@ -2129,7 +2140,7 @@ public class ChooserActivity extends ResolverActivity implements return null; } - if (getPersonalProfileUserHandle() == userHandle) { + if (getPersonalProfileUserHandle().equals(userHandle)) { if (mPersonalAppPredictor != null) { return mPersonalAppPredictor; } @@ -2155,7 +2166,7 @@ public class ChooserActivity extends ResolverActivity implements .getSystemService(AppPredictionManager.class); AppPredictor appPredictionSession = appPredictionManager.createAppPredictionSession( appPredictionContext); - if (getPersonalProfileUserHandle() == userHandle) { + if (getPersonalProfileUserHandle().equals(userHandle)) { mPersonalAppPredictor = appPredictionSession; } else { mWorkAppPredictor = appPredictionSession; @@ -2555,7 +2566,7 @@ public class ChooserActivity extends ResolverActivity implements ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter; if (chooserListAdapter.getUserHandle() - == mChooserMultiProfilePagerAdapter.getCurrentUserHandle()) { + .equals(mChooserMultiProfilePagerAdapter.getCurrentUserHandle())) { mChooserMultiProfilePagerAdapter.getActiveAdapterView() .setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter()); mChooserMultiProfilePagerAdapter @@ -3584,6 +3595,10 @@ public class ChooserActivity extends ResolverActivity implements ? 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 74ae29117b59..0ea855a6b7a9 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.content.pm.ShortcutInfo; 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.DisplayResolveInfo; 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 @@ public class ChooserListAdapter extends ResolverListAdapter { // 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 @@ public class ChooserListAdapter extends ResolverListAdapter { void refreshListView() { if (mListViewDataChanged) { + if (mAppendDirectShareEnabled) { + appendServiceTargetsWithQuota(); + } super.notifyDataSetChanged(); } mListViewDataChanged = false; @@ -198,6 +217,10 @@ public class ChooserListAdapter extends ResolverListAdapter { 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 class ChooserListAdapter extends ResolverListAdapter { */ 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 @@ public class ChooserListAdapter extends ResolverListAdapter { } } + /** + * 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 class ChooserListAdapter extends ResolverListAdapter { */ 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 2167b1ebd473..c6a00f3e46b4 100644 --- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java @@ -93,10 +93,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd @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 907ea55d52a0..921882348328 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -34,14 +34,14 @@ interface IAppOpsService { // 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 @@ interface IAppOpsService { // 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 @@ interface IAppOpsService { 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/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index ec371d9a7311..086a718ad337 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1242,12 +1242,12 @@ public class ResolverActivity extends Activity implements } 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(); @@ -1486,7 +1486,8 @@ public class ResolverActivity extends Activity implements DevicePolicyEventLogger .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET) - .setBoolean(activeListAdapter.getUserHandle() == getPersonalProfileUserHandle()) + .setBoolean(activeListAdapter.getUserHandle() + .equals(getPersonalProfileUserHandle())) .setStrings(getMetricsCategory()) .write(); safelyStartActivity(activeProfileTarget); @@ -1778,7 +1779,7 @@ public class ResolverActivity extends Activity implements @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 +1820,7 @@ public class ResolverActivity extends Activity implements 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 61a404e3bc5f..579abeecad13 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -153,6 +153,14 @@ public class ResolverListAdapter extends BaseAdapter { 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 022aded188fa..51dce5547890 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -367,6 +367,14 @@ public class ResolverListController { 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 0440f5e92ce4..578f6ae63ff1 100644 --- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java @@ -103,10 +103,10 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA @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 01e0622063cd..286945037ab7 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.List; 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 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator 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/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 086b9d83fed5..837cc466a895 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 @@ public final class SystemUiDeviceConfigFlags { "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/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 94924a57e3f2..ff03f1a1a2ab 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -140,6 +140,33 @@ public final class Zygote { */ 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 @@ public final class Zygote { /** 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 @@ public final class Zygote { * @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 @@ public final class Zygote { 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 @@ public final class Zygote { 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 @@ public final class Zygote { * 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 @@ public final class Zygote { 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 @@ public final class Zygote { 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 37f570bba238..1a63765fcaa6 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -227,6 +227,11 @@ class ZygoteArguments { 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 @@ class ZygoteArguments { } } 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 4949811bbe17..bc8dfd4aa402 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -258,7 +258,7 @@ class ZygoteConnection { 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 54cf693490e9..ec1f516df5f3 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -125,12 +125,6 @@ public class ZygoteInit { 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 @@ public class ZygoteInit { 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 @@ public class ZygoteInit { 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 @@ public class ZygoteInit { } /** - * 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 @@ public class ZygoteInit { /** * 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 @@ public class ZygoteInit { 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 @@ public class ZygoteInit { classPathForElement = encodeSystemServerClassPath( classPathForElement, classPathElement); } - - return compiledSomething; } /** @@ -794,6 +761,10 @@ public class ZygoteInit { * 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 000000000000..ebfea450af88 --- /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 380a20c88d56..5b79b184b6a4 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -136,7 +136,8 @@ oneway interface IStatusBar // 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 9907b9975afe..1dbd69c67831 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -105,7 +105,8 @@ interface IStatusBarService // 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 9b2bcfbe89c7..488b18db66d6 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 class CollectionUtils { */ 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 class CollectionUtils { */ 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 @@ public class CollectionUtils { * @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 class CollectionUtils { */ 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 @@ public class CollectionUtils { * @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 @@ public class CollectionUtils { * @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 7f567b948eb2..dd64c402fdbf 100644 --- a/core/java/com/android/internal/util/Parcelling.java +++ b/core/java/com/android/internal/util/Parcelling.java @@ -221,6 +221,33 @@ public interface Parcelling<T> { } } + 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/view/inline/IInlineContentCallback.aidl b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl index 29bdf5661eaf..feb3f026b806 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 @@ import android.view.SurfaceControlViewHost; */ 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 74ad81566ef4..bd0623e1144e 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.ImageView; 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 @@ public class CachingIconView extends ImageView { 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 @@ public class CachingIconView extends ImageView { 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 000000000000..73da6007156a --- /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 000000000000..149d05641a0b --- /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 c9a916187d33..c68da97ab3b4 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -55,8 +55,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou 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 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou 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 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou 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 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou 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 class MessagingGroup extends LinearLayout implements MessagingLinearLayou public void setAvatar(Icon icon) { mAvatarIcon = icon; - mAvatarView.setImageIcon(icon); + if (mShowingAvatar || icon == null) { + mAvatarView.setImageIcon(icon); + } mAvatarSymbol = ""; mAvatarName = ""; } @@ -220,13 +262,20 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou 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 class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public CharSequence getSenderName() { - return mSenderName.getText(); + return mSenderName; } public static void dropCache() { @@ -310,7 +359,12 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou @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 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou 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 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mTextColor = messageTextColor; mSendingTextColor = calculateSendingTextColor(); updateMessageColor(); - mSenderName.setTextColor(senderTextColor); + mSenderView.setTextColor(senderTextColor); } public void setLayoutColor(int layoutColor) { @@ -506,13 +589,17 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } 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 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou 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 class MessagingGroup extends LinearLayout implements MessagingLinearLayou 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 64650a7ebc2f..c243f3b583e5 100644 --- a/core/java/com/android/internal/widget/MessagingImageMessage.java +++ b/core/java/com/android/internal/widget/MessagingImageMessage.java @@ -120,7 +120,7 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage 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 f6089589a994..3fb5d43bea5a 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -58,7 +58,8 @@ import java.util.regex.Pattern; * 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 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer mNameReplacement = nameReplacement; } + /** + * Set this layout to show the collapsed representation. + * + * @param isCollapsed is it collapsed + */ + @RemotableViewMethod + 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 setDisplayImagesAtEnd(boolean atEnd) { - mDisplayImagesAtEnd = atEnd; + public void setConversationTitle(CharSequence conversationTitle) { + // Unused } @RemotableViewMethod @@ -371,6 +392,15 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer 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 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer 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 0c8613b460f6..ac04862d9a7d 100644 --- a/core/java/com/android/internal/widget/MessagingLinearLayout.java +++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java @@ -43,7 +43,7 @@ public class MessagingLinearLayout extends ViewGroup { 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 @@ public class MessagingLinearLayout extends ViewGroup { 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 @@ public class MessagingLinearLayout extends ViewGroup { 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 @@ public class MessagingLinearLayout extends ViewGroup { 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 @@ public class MessagingLinearLayout extends ViewGroup { 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 @@ public class MessagingLinearLayout extends ViewGroup { 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 @@ public class MessagingLinearLayout extends ViewGroup { 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 @@ public class MessagingLinearLayout extends ViewGroup { 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 c32d3705bba7..8c8437951402 100644 --- a/core/java/com/android/internal/widget/MessagingMessage.java +++ b/core/java/com/android/internal/widget/MessagingMessage.java @@ -32,7 +32,7 @@ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { **/ 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 4081a866f993..d778c5967046 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.text.Layout; 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 @@ public class MessagingTextMessage extends ImageFloatingTextView implements Messa 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 39f82a5fb349..a49980696e6b 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.annotation.Nullable; 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 @@ import android.widget.RemoteViews; @RemoteViews.RemoteView public class NotificationExpandButton extends ImageView { + private int mOriginalNotificationColor; + public NotificationExpandButton(Context context) { super(context); } @@ -56,6 +58,15 @@ public class NotificationExpandButton extends ImageView { 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 e352b45ef413..7b154a54fc85 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.view.View; 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 @@ import android.widget.RemoteViews; @RemoteViews.RemoteView public class RemeasuringLinearLayout extends LinearLayout { + private ArrayList<View> mMatchParentViews = new ArrayList<>(); + public RemeasuringLinearLayout(Context context) { super(context); } @@ -53,6 +57,8 @@ public class RemeasuringLinearLayout extends LinearLayout { 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 @@ public class RemeasuringLinearLayout extends LinearLayout { } 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/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 2128f99ff609..ea3c0fa9fc3c 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -121,16 +121,11 @@ typedef const std::function<void(std::string)>& fail_fn_t; 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 @@ static const std::array<const std::string, MOUNT_EXTERNAL_COUNT> ExternalStorage // 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 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list, 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, 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 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( 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 @@ static void com_android_internal_os_Zygote_nativeSpecializeAppProcess( 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 jint com_android_internal_os_Zygote_nativeParseSigChld(JNIEnv* env, jclas 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 @@ static const JNINativeMethod gMethods[] = { {"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 @@ int register_com_android_internal_os_Zygote(JNIEnv* env) { 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 9abe92330cb0..42437d5b44a0 100644 --- a/core/proto/android/app/enums.proto +++ b/core/proto/android/app/enums.proto @@ -104,3 +104,108 @@ enum ProcessStateEnum { 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 030483bc4c33..ef6eb38043f6 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -724,6 +724,16 @@ enum Action { // 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 4a7d04361a5b..e8ae1b3c9dc5 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -109,6 +109,7 @@ message ApplicationInfoProto { } 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/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 3d8108d2a4c8..684a29249294 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 @@ enum EventId { 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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 071bb11d7738..7a3ec9555f7c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3756,6 +3756,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" /> @@ -5003,15 +5009,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" 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 000000000000..0dd0dcda40fb --- /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 124e99e3a4bb..ca4f0ed27a0b 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 @@ Copyright (C) 2015 The Android Open Source Project 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 847e3269398d..a080ce43cfec 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 @@ Copyright (C) 2015 The Android Open Source Project 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/layout/conversation_face_pile_layout.xml b/core/res/res/layout/conversation_face_pile_layout.xml new file mode 100644 index 000000000000..1db38702f926 --- /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 f5fa1b6a795a..6f36aae8a1d4 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 000000000000..2348deeed7fb --- /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 483b479538a1..3188861a52a5 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 619b3927e8d5..176f2890c0e0 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 diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index c2451319bdc8..f3ca5ac9a4c4 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1541,6 +1541,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, @@ -1552,7 +1574,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}, @@ -1806,31 +1828,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 @@ -2312,6 +2354,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/config.xml b/core/res/res/values/config.xml index 6f468e03e0b7..a2aa4929475a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4421,4 +4421,8 @@ <!-- The max scale for the wallpaper when it's zoomed in --> <item name="config_wallpaperMaxScale" format="float" type="dimen">1</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 2faa0c9c8036..15ef09c4277a 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> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 39e81975fce6..7230cc49ac5b 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"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 789628d63d1b..f101f590cab1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5440,6 +5440,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 966f495c96e5..64768cf4c730 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -146,7 +146,7 @@ easier. <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 @@ easier. <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 63ac0e6bfc3e..2415837cf826 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -504,7 +504,7 @@ please see styles_device_defaults.xml. <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 a9008d78e19a..4c0dd8dd3e4e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3615,6 +3615,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" /> @@ -3876,6 +3877,34 @@ <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" /> diff --git a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java index 44f64079d831..37b2817928aa 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.IOException; 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 @@ public class SourceStampVerifierTest { 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 50cd5a3b01b5..fe25e79df44e 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -123,7 +123,7 @@ public class InsetsAnimationControlImplTest { 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,7 +182,7 @@ public class InsetsAnimationControlImplTest { @Test public void testCancelled() { - mController.onCancelled(); + mController.cancel(); try { mController.setInsetsAndAlpha(Insets.NONE, 1f /*alpha */, 0f /* fraction */); fail("Expected exception to be thrown"); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 023fc1736aca..42ab2e791641 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 android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 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.Point; 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.animation.LinearInterpolator; 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.InOrder; import org.mockito.Mockito; import java.util.concurrent.CountDownLatch; -import java.util.function.Supplier; /** * Tests for {@link InsetsController}. @@ -169,11 +170,8 @@ public class InsetsControllerTest { @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 @@ public class InsetsControllerTest { 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,14 +193,12 @@ public class InsetsControllerTest { @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(); @@ -224,7 +218,7 @@ public class InsetsControllerTest { 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(); @@ -262,11 +256,8 @@ public class InsetsControllerTest { @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 @@ public class InsetsControllerTest { @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 @@ public class InsetsControllerTest { 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 @@ public class InsetsControllerTest { @Test public void testStartImeAnimationAfterGettingControl() { - InsetsSourceControl control = - new InsetsSourceControl(ITYPE_IME, mLeash, new Point()); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { @@ -471,7 +458,7 @@ public class InsetsControllerTest { 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 @@ public class InsetsControllerTest { @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 @@ public class InsetsControllerTest { @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 @@ public class InsetsControllerTest { @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(); @@ -567,7 +548,8 @@ public class InsetsControllerTest { 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 @@ public class InsetsControllerTest { 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(); @@ -611,7 +594,8 @@ public class InsetsControllerTest { 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(); @@ -632,8 +616,10 @@ public class InsetsControllerTest { 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(); @@ -655,12 +641,23 @@ public class InsetsControllerTest { 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 9797178fca6e..33f859ef6663 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.navigationBars; 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.platform.test.annotations.Presubmit; 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 class PendingInsetsControllerTest { public void testControl() { WindowInsetsAnimationControlListener listener = mock(WindowInsetsAnimationControlListener.class); - CancellationSignal signal = mPendingInsetsController.controlWindowInsetsAnimation( - systemBars(), 0, new LinearInterpolator(), listener); + CancellationSignal cancellationSignal = new CancellationSignal(); + mPendingInsetsController.controlWindowInsetsAnimation( + systemBars(), 0, new LinearInterpolator(), cancellationSignal, listener); verify(listener).onCancelled(); - assertTrue(signal.isCanceled()); + assertFalse(cancellationSignal.isCanceled()); } @Test @@ -106,10 +108,11 @@ public class PendingInsetsControllerTest { 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 02a88fc8aecb..5ea071835de2 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 com.google.common.truth.Truth.assertThat; 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 @@ public class ContentCaptureSessionTest { } @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/SystemTextClassifierMetadataTest.java b/core/tests/coretests/src/android/view/textclassifier/SystemTextClassifierMetadataTest.java new file mode 100644 index 000000000000..e4cfc53cc53a --- /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/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java index d54402975f65..39ededaf1f67 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 @@ public class TextClassificationTest { .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 @@ public class TextClassificationTest { 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/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java index d0d32e3a507a..31f8029550dd 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 @@ public final class TextLanguageTest { 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 @@ public final class TextLanguageTest { 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 ec5813fd4123..4f0b44bfb864 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 @@ public class TextLinksTest { .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 @@ public class TextLinksTest { 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 30cc4e8990e0..82788c806fed 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 @@ public class TextSelectionTest { .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 @@ public class TextSelectionTest { 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/com/android/internal/app/AbstractResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java index 36dd3e4e72b9..03ed68cd1205 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 @@ public class AbstractResolverComparatorTest { @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 24e96d4b8463..a6cbc1ae466d 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 @@ public class ChooserActivityTest { /* 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 @@ public class ChooserActivityTest { /* 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 @@ public class ChooserActivityTest { /* 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/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml index 61281eea7134..efe658a80233 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/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 487a6e912238..5466ac8e2cfa 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -62,6 +62,10 @@ applications that come with the platform <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"/> diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 550e41f28f85..ff7049ee565b 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -612,7 +612,7 @@ public class LocationManager { public Location getLastLocation() { try { return mService.getLastLocation(null, mContext.getPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -645,7 +645,7 @@ public class LocationManager { try { return mService.getLastLocation(request, mContext.getPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -742,7 +742,7 @@ public class LocationManager { 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 @@ public class LocationManager { 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 @@ public class LocationManager { 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 @@ public class LocationManager { 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 @@ public class LocationManager { 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 @@ public class LocationManager { GnssStatusListener transport = new GnssStatusListener(); if (mService.registerGnssStatusCallback(transport, mContext.getPackageName(), - mContext.getFeatureId())) { + mContext.getAttributionTag())) { mListenerTransport = transport; return true; } else { @@ -3012,7 +3012,7 @@ public class LocationManager { 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 @@ public class LocationManager { 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 @@ public class LocationManager { 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 @@ public class LocationManager { 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/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 8e39dfae7c57..fb95e98ebf47 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -466,10 +466,6 @@ public abstract class MediaRoute2ProviderService extends Service { 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 37e2ab53a1a1..80545e509900 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -58,7 +58,7 @@ import java.util.stream.Collectors; // 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 @@ public class MediaRouter2 { 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") diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 11c9fe199521..88bcd6aaad95 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -50,7 +50,7 @@ import java.util.stream.Collectors; * 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 @@ public class MediaRouter2Manager { final String mPackageName; - private Context mContext; + private final Context mContext; @GuardedBy("sLock") private Client mClient; private final IMediaRouterService mMediaRouterService; @@ -74,7 +74,7 @@ public class MediaRouter2Manager { @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/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java index 705d5207cc5f..197c1c5814f1 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java @@ -144,17 +144,17 @@ public class DvbcFrontendSettings extends FrontendSettings { 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 +172,8 @@ public class DvbcFrontendSettings extends FrontendSettings { * Gets Inner Forward Error Correction. */ @InnerFec - public long getFec() { - return mFec; + public long getInnerFec() { + return mInnerFec; } /** * Gets Symbol Rate in symbols per second. @@ -220,7 +220,7 @@ public class DvbcFrontendSettings extends FrontendSettings { */ public static class Builder extends FrontendSettings.Builder<Builder> { private int mModulation; - private long mFec; + private long mInnerFec; private int mSymbolRate; private int mOuterFec; private int mAnnex; @@ -241,8 +241,8 @@ public class DvbcFrontendSettings extends FrontendSettings { * Sets Inner Forward Error Correction. */ @NonNull - public Builder setFec(@InnerFec long fec) { - mFec = fec; + public Builder setInnerFec(@InnerFec long fec) { + mInnerFec = fec; return this; } /** @@ -283,8 +283,8 @@ public class DvbcFrontendSettings extends FrontendSettings { */ @NonNull public DvbcFrontendSettings build() { - return new DvbcFrontendSettings(mFrequency, mModulation, mFec, mSymbolRate, mOuterFec, - mAnnex, mSpectralInversion); + return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate, + mOuterFec, mAnnex, mSpectralInversion); } @Override diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index 63de03344219..ea91ee9250b5 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 @@ public class FrontendStatus { * 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/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 20efaa1e0833..77cac6ef7e2e 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -19,6 +19,8 @@ package android.media.tv.tunerresourcemanager; 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 @@ interface ITunerResourceManager { 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 @@ interface ITunerResourceManager { 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 000000000000..919a215a9ce5 --- /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 000000000000..34a77616f62e --- /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 000000000000..fbafb3bc010e --- /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 000000000000..58162879f8cf --- /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 7c11ed485ced..9dddcd4eaa3b 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -60,11 +60,7 @@ public class TunerResourceManager { 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; private final ITunerResourceManager mService; private final int mUserId; @@ -260,6 +256,71 @@ public class TunerResourceManager { } /** + * 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 +406,36 @@ public class TunerResourceManager { } /** + * 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/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index 6354ccd7b3b0..ebd7658381ee 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 @@ public class CameraBinderTest extends AndroidTestCase { 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 466c5f4ba980..bf3e74602dbe 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 @@ public class CameraDeviceBinderTest extends AndroidTestCase { 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 95465007d5ea..d9806f3cbb1d 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 230b9e4bfaad..a97baafac992 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.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE; 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 @@ public class MediaRouter2ManagerTest { // unregister callbacks clearCallbacks(); - SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance(); + StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance(); if (instance != null) { instance.setProxy(null); } @@ -161,8 +161,8 @@ public class MediaRouter2ManagerTest { 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 @@ public class MediaRouter2ManagerTest { 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 index f05d8ad79dee..6d46ba582ddc 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 java.util.Objects; 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 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService private int mNextSessionId = 1000; @GuardedBy("sLock") - private static SampleMediaRoute2ProviderService sInstance; + private static StubMediaRoute2ProviderService sInstance; private Proxy mProxy; private void initializeRoutes() { @@ -127,7 +127,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute); } - public static SampleMediaRoute2ProviderService getInstance() { + public static StubMediaRoute2ProviderService getInstance() { synchronized (sLock) { return sInstance; } 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 f93faeb0c7ae..ace50f30663d 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 @@ public class TileUtils { 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/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 18eb1879d7cf..3c7856048860 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 class A2dpProfile implements LocalBluetoothProfile { } 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 class A2dpProfile implements LocalBluetoothProfile { } 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 class A2dpProfile implements LocalBluetoothProfile { } 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 class A2dpProfile implements LocalBluetoothProfile { } 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/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 90f55dd6f38d..44b481dea77d 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 @@ public class LocalMediaManager implements BluetoothCallback { @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<>(); @@ -108,14 +110,12 @@ public class LocalMediaManager implements BluetoothCallback { new InfoMediaManager(context, packageName, notification, mLocalBluetoothManager); } - @VisibleForTesting - LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager, + public LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager, InfoMediaManager infoMediaManager, String packageName) { mContext = context; mLocalBluetoothManager = localBluetoothManager; mInfoMediaManager = infoMediaManager; mPackageName = packageName; - } /** @@ -128,6 +128,7 @@ public class LocalMediaManager implements BluetoothCallback { final CachedBluetoothDevice cachedDevice = ((BluetoothMediaDevice) device).getCachedDevice(); if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) { + device.setState(MediaDeviceState.STATE_CONNECTING); cachedDevice.connect(); return; } @@ -142,6 +143,7 @@ public class LocalMediaManager implements BluetoothCallback { mCurrentConnectedDevice.disconnect(); } + device.setState(MediaDeviceState.STATE_CONNECTING); if (TextUtils.isEmpty(mPackageName)) { mInfoMediaManager.connectDeviceWithoutPackageName(device); } else { @@ -423,6 +425,7 @@ public class LocalMediaManager implements BluetoothCallback { 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 +443,11 @@ public class LocalMediaManager implements BluetoothCallback { @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 33c3d7e039b3..39e6a129a992 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 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { int mType; private int mConnectedRecord; + private int mState; protected final Context mContext; protected final MediaRoute2Info mRouteInfo; @@ -200,6 +201,22 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { } /** + * 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/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java index 68a3729432d8..245b7843110b 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 static org.mockito.Mockito.when; 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 @@ public class RecentLocationAccessesTest { 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 3f8d758ae64f..cc9b93141d45 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 static org.mockito.Mockito.when; 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.Process; 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 @@ public class RecentLocationAppsTest { 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/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 18d8f14eb88d..f825ec520d49 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 static org.mockito.Mockito.when; 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 @@ import java.util.List; @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 @@ public class LocalMediaManagerTest { 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,11 +99,18 @@ public class LocalMediaManagerTest { 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(); @@ -123,6 +145,32 @@ public class LocalMediaManagerTest { } @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 +436,21 @@ public class LocalMediaManagerTest { } @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 +460,26 @@ public class LocalMediaManagerTest { } @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 3f29b72b978d..4b08387275be 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 @@ public class MediaDeviceTest { 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/SystemUI/res/color/control_background.xml b/packages/SystemUI/res/color/control_background.xml deleted file mode 100644 index 977310cfae01..000000000000 --- 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 12994646a346..000000000000 --- 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 977310cfae01..000000000000 --- 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 2709ebe4dcfd..000000000000 --- 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 64b57c5aac2b..3acebc12a807 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 @@ 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: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 374e3b120e4b..c58f572e1bbb 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/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 80c1ac8e3496..56e2b061ebc2 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 20cafd046452..82224dff88dd 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -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 291db65da225..e45cbecd3aa1 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 18fec29dd3cf..35430733a118 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2631,10 +2631,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 47709108db1b..20b88a1a550d 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 4885ade23f6a..07bd3a0567e6 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 class QuickStepContract { 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 @@ public class QuickStepContract { 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 @@ public class QuickStepContract { 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 d33c653f2cb7..29100ef8f70f 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 @@ public class SurfaceViewRequestReceiver { 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/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 3cf07d14cbf2..ba8a1a945a77 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.NEW_INSETS_MODE_FULL; 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.app.admin.DevicePolicyManager; 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 537a812d19cb..a8a3caecc780 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -50,6 +50,7 @@ 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; @@ -126,8 +127,9 @@ public class ScreenDecorations extends SystemUI implements Tunable { 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() { @@ -194,6 +196,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { 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(); @@ -572,15 +576,22 @@ public class ScreenDecorations extends SystemUI implements Tunable { 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); } } @@ -631,7 +642,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { } private boolean hasRoundedCorners() { - return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0; + return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0 + || mIsRoundedCornerMultipleRadius; } private boolean shouldShowRoundedCorner(@BoundsPosition int pos) { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 5077e18b06e4..cc4ee89f2208 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.keyguard.KeyguardUpdateMonitor; 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 com.android.systemui.statusbar.policy.DeviceProvisionedController; 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 class AssistManager { 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 @@ public class AssistManager { 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 @@ public class AssistManager { CommandQueue commandQueue, PhoneStateMonitor phoneStateMonitor, OverviewProxyService overviewProxyService, - ConfigurationController configurationController) { + ConfigurationController configurationController, + Lazy<SysUiState> sysUiState) { mContext = context; mDeviceProvisionedController = controller; mCommandQueue = commandQueue; @@ -206,6 +217,8 @@ public class AssistManager { mUiController = new DefaultUiController(mContext); + mSysUiState = sysUiState; + overviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() { @Override public void onAssistantProgress(float progress) { @@ -243,8 +256,16 @@ public class AssistManager { 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 cbb19824eadf..0018d33bdacb 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 @@ public class AuthContainerView extends LinearLayout // 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 @@ public class AuthContainerView extends LinearLayout String mOpPackageName; int mModalityMask; boolean mSkipIntro; + long mOperationId; } public static class Builder { @@ -149,6 +152,11 @@ public class AuthContainerView extends LinearLayout 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 @@ public class AuthContainerView extends LinearLayout 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 @@ public class AuthContainerView extends LinearLayout mCredentialView.setContainerView(this); mCredentialView.setUserId(mConfig.mUserId); + mCredentialView.setOperationId(mConfig.mOperationId); mCredentialView.setEffectiveUserId(mEffectiveUserId); mCredentialView.setCredentialType(credentialType); mCredentialView.setCallback(mCredentialCallback); @@ -558,7 +568,7 @@ public class AuthContainerView extends LinearLayout 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 6149b0b8bfd4..c30477c77bbb 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_FACE; 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } @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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } } - 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, args.arg3 = requireConfirmation; args.argi2 = userId; args.arg4 = opPackageName; + args.arg5 = operationId; boolean skipAnimation = false; if (mCurrentDialog != null) { @@ -354,6 +368,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, userId, type, opPackageName, - skipAnimation); + skipAnimation, + operationId); if (newDialog == null) { Log.e(TAG, "Unsupported type: " + type); @@ -429,7 +445,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } 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 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, .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 82c8a469a057..b986f6c9e680 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 @@ public class AuthCredentialPasswordView extends AuthCredentialView 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 03136a4b6c0f..6d16f4397115 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 @@ public class AuthCredentialPatternView extends AuthCredentialView { 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 48c66215bdde..0d9d4269230f 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 @@ import com.android.systemui.R; /** * 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 @@ public abstract class AuthCredentialView extends LinearLayout { 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 @@ public abstract class AuthCredentialView extends LinearLayout { mUserId = userId; } + void setOperationId(long operationId) { + mOperationId = operationId; + } + void setEffectiveUserId(int effectiveUserId) { mEffectiveUserId = effectiveUserId; } @@ -245,10 +250,13 @@ public abstract class AuthCredentialView extends LinearLayout { 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 12bb1228a53b..a47621d12122 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 @@ public interface AuthDialogCallback { /** * 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/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index cf5a4d3840cc..54b83c0b134d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -207,12 +207,13 @@ public class BubbleData { // 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 +226,14 @@ public class BubbleData { 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); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 0b7dbd2bfa2e..e666fb5f1af9 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 @@ public class BubbleStackView extends FrameLayout { private int mOrientation = Configuration.ORIENTATION_UNDEFINED; + @Nullable private BubbleOverflow mBubbleOverflow; private boolean mShouldShowUserEducation; @@ -470,7 +471,8 @@ public class BubbleStackView extends FrameLayout { 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); @@ -1387,7 +1389,7 @@ public class BubbleStackView extends FrameLayout { Log.d(TAG, "onBubbleDragStart: bubble=" + bubble); } - if (bubble.equals(mBubbleOverflow.getIconView())) { + if (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView())) { return; } @@ -1403,7 +1405,8 @@ public class BubbleStackView extends FrameLayout { /** Called with the coordinates to which an individual bubble has been dragged. */ public void onBubbleDragged(View bubble, float x, float y) { - if (!mIsExpanded || mIsExpansionAnimating || bubble.equals(mBubbleOverflow.getIconView())) { + if (!mIsExpanded || mIsExpansionAnimating + || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) { return; } @@ -1418,7 +1421,8 @@ public class BubbleStackView extends FrameLayout { Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble); } - if (!mIsExpanded || mIsExpansionAnimating || bubble.equals(mBubbleOverflow.getIconView())) { + if (!mIsExpanded || mIsExpansionAnimating + || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) { return; } 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 3eaa90c985d7..9b5dc31eea2f 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 @@ public class ExpandedAnimationController 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 86387f1cc546..b63ba6fe5d14 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.FileDescriptor; 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 @@ -241,9 +242,15 @@ public class StackAnimationController extends } }; + /** 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; + } /** @@ -669,6 +676,8 @@ public class StackAnimationController extends 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 +745,7 @@ public class StackAnimationController extends 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 +767,7 @@ public class StackAnimationController extends .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 +988,11 @@ public class StackAnimationController extends 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/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index fc5663fe8c97..b4392067d953 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 com.android.systemui.R 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 @@ class ControlViewHolder( 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 @@ class ControlViewHolder( } 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 @@ class ControlViewHolder( 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 bde966ca067e..138cd47caae7 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 @@ private class ItemAdapter( 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 56267beb1b71..27e46497b20a 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 IconState(val disabledResourceId: Int, val enabledResourceId: Int) { } } -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 @@ data class RenderInfo(val icon: Drawable, val foreground: Int, val background: I icon = iconMap.get(resourceId) if (icon == null) { icon = context.resources.getDrawable(resourceId, null) + icon.mutate() iconMap.put(resourceId, icon) } } @@ -94,12 +99,13 @@ private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE 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 6595b55a691d..c495c58fff2a 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 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 @@ class ToggleRangeBehavior : Behavior { 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 @@ class ToggleRangeBehavior : Behavior { 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 @@ class ToggleRangeBehavior : Behavior { } } - 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 @@ class ToggleRangeBehavior : Behavior { 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/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index c45063a52f78..f6fccc00bf99 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 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi } 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 27c81ce5321d..b99d7655c425 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.ActivityStarter; 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 java.util.concurrent.Executor; 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mIActivityManager = iActivityManager; mTelecomManager = telecomManager; mMetricsLogger = metricsLogger; - mBlurUtils = blurUtils; + mDepthController = depthController; mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; @@ -237,6 +240,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mIWindowManager = iWindowManager; mBackgroundExecutor = backgroundExecutor; mControlsListingController = controlsListingController; + mBlurUtils = blurUtils; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -268,8 +272,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ public void onDismiss(DialogInterface dialog) { if (mDialog == dialog) { mDialog = null; @@ -932,17 +947,19 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } - /** {@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 class GlobalActionsDialog implements DialogInterface.OnDismissListener, */ 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } /** - * 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } /** - * 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } /** - * 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } /** - * 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, /** * 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, float animatedValue = animation.getAnimatedFraction(); int alpha = (int) (animatedValue * mScrimAlpha * 255); mBackgroundDrawable.setAlpha(alpha); - mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(), - mBlurUtils.blurRadiusOfRatio(animatedValue)); + mDepthController.updateGlobalDialogVisibility(animatedValue, + mGlobalActionsLayout); }) .start(); if (mControlsUiController != null) { @@ -1822,8 +1840,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, float animatedValue = 1f - animation.getAnimatedFraction(); int alpha = (int) (animatedValue * mScrimAlpha * 255); mBackgroundDrawable.setAlpha(alpha); - mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(), - mBlurUtils.blurRadiusOfRatio(animatedValue)); + mDepthController.updateGlobalDialogVisibility(animatedValue, + mGlobalActionsLayout); }) .start(); dismissPanel(); @@ -1914,8 +1932,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } /** - * 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 bbb57bdb33f7..12955a153360 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -149,7 +149,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks } 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.blurRadiusOfRatio(1)); } else { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 3d708a91dbf0..226ac16cf1f2 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.dagger.qualifiers.UiBackground; 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 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { private boolean mLockLater; private boolean mShowHomeOverLockscreen; + private boolean mInGestureNavigationMode; private boolean mWakeAndUnlocking; private IKeyguardDrawnCallback mDrawnCallback; @@ -720,7 +723,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { 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 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); + mInGestureNavigationMode = + QuickStepContract.isGesturalMode(navigationModeController.addListener(mode -> { + mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode); + })); } public void userActivity() { @@ -2013,7 +2021,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { // 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 ae380b72f5e0..c281ece59c5a 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 @@ public class WorkLockActivityController { 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 9be478639ed8..7a63a5e406f6 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.dump.DumpManager; 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 @@ public class KeyguardModule { PowerManager powerManager, TrustManager trustManager, @UiBackground Executor uiBgExecutor, - DeviceConfigProxy deviceConfig) { + DeviceConfigProxy deviceConfig, + NavigationModeController navigationModeController) { return new KeyguardViewMediator( context, falsingManager, @@ -78,6 +80,7 @@ public class KeyguardModule { uiBgExecutor, powerManager, trustManager, - deviceConfig); + deviceConfig, + navigationModeController); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index 67802bc9888d..4bfcf2229e3e 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 com.android.internal.annotations.VisibleForTesting; 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 @@ public class PipAnimationController { 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 @@ public class PipAnimationController { @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 @@ public class PipAnimationController { } private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) { + animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper); animator.setInterpolator(mFastOutSlowInInterpolator); animator.setFloatValues(FRACTION_START, FRACTION_END); return animator; @@ -152,9 +157,10 @@ public class PipAnimationController { 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 @@ public class PipAnimationController { 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 @@ public class PipAnimationController { } @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 @@ public class PipAnimationController { 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 @@ public class PipAnimationController { 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 fb348f483c46..88491b7073da 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 @@ public class PipBoundsHandler { 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 @@ public class PipBoundsHandler { private int mDefaultMinSize; private Point mScreenEdgeInsets; private int mCurrentMinSize; + private Size mOverrideMinimalSize; private boolean mIsImeShowing; private int mImeHeight; @@ -226,11 +227,14 @@ public class PipBoundsHandler { /** * @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) { destinationBounds = new Rect(defaultBounds); + if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) { + mOverrideMinimalSize = minimalSize; + } } else { destinationBounds = new Rect(bounds); } @@ -335,7 +339,6 @@ public class PipBoundsHandler { */ 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 @@ public class PipBoundsHandler { 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 000000000000..21f9301fe794 --- /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 51113acd0e82..4076c1bbb367 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 @@ package com.android.systemui.pip; 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,13 @@ import android.app.ActivityTaskManager; 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; @@ -47,9 +47,7 @@ import com.android.systemui.R; import com.android.systemui.pip.phone.PipUpdateThread; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.function.Consumer; @@ -79,8 +77,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { private final PipAnimationController mPipAnimationController; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); private final Rect mLastReportedBounds = new Rect(); - private final int mCornerRadius; - private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>(); + private final int mEnterExitAnimationDuration; + private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = @@ -172,14 +170,20 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { 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 +206,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { 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; @@ -232,17 +222,15 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { throw new RuntimeException("Unable to get leash", e); } final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); - 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 +246,9 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { Log.wtf(TAG, "Unrecognized token: " + token); return; } - final Rect boundsToRestore = mBoundsToRestore.remove(mToken.asBinder()); - scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore, - TRANSITION_DIRECTION_TO_FULLSCREEN, DURATION_DEFAULT_MS, null); + scheduleAnimateResizePip(mLastReportedBounds, + info.configuration.windowConfiguration.getBounds(), + TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null); mInPip = false; } @@ -276,9 +264,10 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { 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 +294,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { 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 +312,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { * {@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 +319,20 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } /** - * 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 +350,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { // 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 +378,16 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { 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, @@ -447,12 +430,19 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { 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/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 32e9a03dfac9..020627a20458 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.broadcast.BroadcastDispatcher; 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; @@ -210,7 +211,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio FloatingContentCoordinator floatingContentCoordinator, DeviceConfigProxy deviceConfig, PipBoundsHandler pipBoundsHandler, - PipSnapAlgorithm pipSnapAlgorithm) { + PipSnapAlgorithm pipSnapAlgorithm, + PipSurfaceTransactionHelper surfaceTransactionHelper) { mContext = context; mActivityManager = ActivityManager.getService(); @@ -224,7 +226,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio 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 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio 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/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 33760be3f469..449a2bca6abb 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 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, 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 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, 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 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, 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 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, cancelAnimations(); mAnimatedBoundsPhysicsAnimator - .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds)) + .withEndActions(() -> mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds)) .addUpdateListener(mResizePipUpdateListener) .start(); } @@ -437,7 +437,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * {@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/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 0c5a4d782e69..050acd5a8728 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.ActivityTaskManager.INVALID_STACK_ID; 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.UiOffloadThread; 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 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio 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 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Inject public PipManager(Context context, BroadcastDispatcher broadcastDispatcher, - PipBoundsHandler pipBoundsHandler) { + PipBoundsHandler pipBoundsHandler, + PipSurfaceTransactionHelper surfaceTransactionHelper) { if (mInitialized) { return; } @@ -238,7 +239,10 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio 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 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio 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/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 9ab47145f92d..bf72b33ada42 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 android.widget.LinearLayout; 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; @@ -98,6 +100,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private final LinearLayout mMediaCarousel; private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>(); private final NotificationMediaManager mNotificationMediaManager; + private final LocalBluetoothManager mLocalBluetoothManager; private final Executor mBackgroundExecutor; private LocalMediaManager mLocalMediaManager; private MediaDevice mDevice; @@ -157,7 +160,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne BroadcastDispatcher broadcastDispatcher, QSLogger qsLogger, NotificationMediaManager notificationMediaManager, - @Background Executor backgroundExecutor + @Background Executor backgroundExecutor, + @Nullable LocalBluetoothManager localBluetoothManager ) { super(context, attrs); mContext = context; @@ -165,6 +169,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mDumpManager = dumpManager; mNotificationMediaManager = notificationMediaManager; mBackgroundExecutor = backgroundExecutor; + mLocalBluetoothManager = localBluetoothManager; setOrientation(VERTICAL); @@ -286,7 +291,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne // 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/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 3da767e51be0..6654b7ab0749 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 @@ package com.android.systemui.qs; 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,6 +27,7 @@ import android.view.Gravity; 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; @@ -77,10 +79,11 @@ public class QuickQSPanel extends QSPanel { BroadcastDispatcher broadcastDispatcher, QSLogger qsLogger, NotificationMediaManager notificationMediaManager, - @Background Executor backgroundExecutor + @Background Executor backgroundExecutor, + @Nullable LocalBluetoothManager localBluetoothManager ) { super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, notificationMediaManager, - backgroundExecutor); + backgroundExecutor, localBluetoothManager); if (mFooter != null) { removeView(mFooter.getView()); } @@ -155,8 +158,6 @@ public class QuickQSPanel extends QSPanel { Dependency.get(TunerService.class).removeTunable(mNumTiles); } - - @Override protected String getDumpableTag() { return TAG; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index ea5ec0503067..d7eab3a6eac4 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.graphics.Rect; 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 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, 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; @@ -173,6 +172,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, @Nullable private ValueAnimator mAnimation = null; + private boolean mPaused = true; + private boolean mPausedTargetAdjusted = false; + private boolean getSecondaryHasFocus(int displayId) { try { IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController() @@ -185,6 +187,14 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, 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) { @@ -193,18 +203,31 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } final boolean splitIsVisible = !mView.isHidden(); mSecondaryHasFocus = getSecondaryHasFocus(displayId); - mTargetAdjusted = splitIsVisible && 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; + } + } } - mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible) - ? ADJUSTED_NONFOCUS_DIM : 0.f; - mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible) - ? ADJUSTED_NONFOCUS_DIM : 0.f; + 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 @@ -259,7 +282,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, @Override public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { - if (mAnimation != null || !inSplitMode()) { + if (mAnimation != null || !inSplitMode() || mPaused) { // Not synchronized with IME anymore, so return. return; } @@ -271,7 +294,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, @Override public void onImeEndPositioning(int displayId, boolean cancelled, SurfaceControl.Transaction t) { - if (mAnimation != null || !inSplitMode()) { + if (mAnimation != null || !inSplitMode() || mPaused) { // Not synchronized with IME anymore, so return. return; } @@ -279,7 +302,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } 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); @@ -342,6 +365,52 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, }); 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(); @@ -493,6 +562,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } 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); @@ -519,12 +589,21 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, /** 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) { @@ -544,7 +623,17 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, // 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); @@ -649,7 +738,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, 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 */); } @@ -660,7 +749,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, 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 4114bb9d055d..131f4e1c9d59 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.Bundle; 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.R; 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(); @@ -627,6 +631,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, 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(), @@ -634,16 +639,21 @@ public class DividerView extends FrameLayout implements OnTouchListener, ? 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(); @@ -667,12 +677,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } }; - Runnable notCancelledEndAction = () -> { - // Reset minimized divider position after unminimized state animation finishes - if (!mDockedStackMinimized && mIsInMinimizeInteraction) { - mIsInMinimizeInteraction = false; - } - }; anim.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @@ -694,15 +698,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, 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); } } }); @@ -899,6 +899,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, public void setMinimizedDockStack(boolean minimized, long animDuration, boolean isHomeStackResizable) { + if (DEBUG) Slog.d(TAG, "setMinDock: " + mDockedStackMinimized + "->" + minimized); mHomeStackResizable = isHomeStackResizable; updateDockSide(); if (!isHomeStackResizable) { @@ -946,6 +947,15 @@ public class DividerView extends FrameLayout implements OnTouchListener, .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; @@ -1061,6 +1071,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, 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), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index c523b7b7655e..866980458cad 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 @@ open class BlurUtils @Inject constructor( ) : 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) @@ -97,7 +99,7 @@ open class BlurUtils @Inject constructor( * @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>) { @@ -106,7 +108,8 @@ open class BlurUtils @Inject constructor( 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 3fe348f3ea02..94afde786e78 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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< 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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< @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 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args.arg3 = requireConfirmation; args.argi2 = userId; args.arg4 = opPackageName; + args.arg5 = operationId; mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args) .sendToTarget(); } @@ -1164,7 +1167,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< 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/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java index 18574f0f891e..ac3523b2fffd 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.ImageView; 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 class MediaTransferManager { 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 0bfcdbdb1118..4759d56099f3 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.graphics.drawable.Icon; 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 @@ public class NotificationHeaderUtil { 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 @@ public class NotificationHeaderUtil { 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 @@ public class NotificationHeaderUtil { 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/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 901ed3f94760..eb8526d0ef91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -58,6 +58,7 @@ class NotificationShadeDepthController @Inject constructor( } lateinit var root: View + private var blurRoot: View? = null private var keyguardAnimator: Animator? = null private var notificationAnimator: Animator? = null private var updateScheduled: Boolean = false @@ -72,6 +73,7 @@ class NotificationShadeDepthController @Inject constructor( return shadeBlurRadius.toFloat() } }) + private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE private var shadeBlurRadius = 0 set(value) { if (field == value) return @@ -84,6 +86,7 @@ class NotificationShadeDepthController @Inject constructor( field = value scheduleUpdate() } + private var globalDialogVisibility = 0f /** * Callback that updates the window blur value and is called only once per frame. @@ -91,9 +94,12 @@ class NotificationShadeDepthController @Inject constructor( private val updateBlurCallback = Choreographer.FrameCallback { updateScheduled = false - val blur = max(shadeBlurRadius, wakeAndUnlockBlurRadius) - blurUtils.applyBlur(root.viewRootImpl, blur) - wallpaperManager.setWallpaperZoomOut(root.windowToken, blurUtils.ratioOfBlurRadius(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)) } /** @@ -162,14 +168,23 @@ class NotificationShadeDepthController @Inject constructor( 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, " ").let { it.println("StatusBarWindowBlurController:") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java index 7b5a70eb5430..2a45bc210580 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 @@ public class ViewTransformationHelper implements TransformableView, 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 @@ public class ViewTransformationHelper implements TransformableView, 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 class ViewTransformationHelper implements TransformableView, 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 b732966b32db..9383f537db45 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.util.Pools; 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 @@ public class MessagingLayoutTransformState extends TransformState { 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 @@ public class MessagingLayoutTransformState extends TransformState { 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 @@ public class MessagingLayoutTransformState extends TransformState { 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 @@ public class MessagingLayoutTransformState extends TransformState { } 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 @@ public class MessagingLayoutTransformState extends TransformState { } private boolean isGone(View view) { + if (view == null) { + return true; + } if (view.getVisibility() == View.GONE) { return true; } @@ -408,7 +430,7 @@ public class MessagingLayoutTransformState extends TransformState { 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/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9a4e789a2e03..f61fe9830939 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.ObjectAnimator; 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; @@ -151,7 +150,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private int mNotificationMinHeight; private int mNotificationMinHeightLarge; private int mNotificationMinHeightMedia; - private int mNotificationMinHeightMessaging; private int mNotificationMaxHeight; private int mIncreasedPaddingBetweenElements; private int mNotificationLaunchHeight; @@ -640,16 +638,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView && 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 { @@ -1057,19 +1049,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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; } @@ -1654,8 +1633,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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, 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 6dd4ff9235c4..91cf285f2262 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.NonNull; 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 @@ public class NotificationContentInflater implements NotificationRowContentBinder 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 @@ public class NotificationContentInflater implements NotificationRowContentBinder // 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 27fd1b2c5aed..8b8a9012cbdc 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 class NotificationContentView extends FrameLayout { } } - 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 000000000000..1e2571b5c801 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt @@ -0,0 +1,100 @@ +/* + * 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 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) + } + + 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 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 5e52c0a5a66f..1d061989a84c 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 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { 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 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { 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 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { 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 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { 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 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { @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 0a1a2fe3ee54..d41f5af6c524 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 @@ -353,8 +353,12 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp @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 c2eff8a6a776..c834e4b376ed 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 @@ -35,6 +35,7 @@ import android.widget.TextView; 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; @@ -61,6 +62,9 @@ public abstract class NotificationViewWrapper implements TransformableView { 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(); 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 745843deeddb..adca10ff7677 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.ViewConfiguration; 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 @@ public class EdgeBackGestureHandler implements DisplayListener, 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 @@ public class EdgeBackGestureHandler implements DisplayListener, private int mRightInset; private int mSysUiFlags; + private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver; + private final NavigationEdgeBackPlugin.BackCallback mBackCallback = new NavigationEdgeBackPlugin.BackCallback() { @Override @@ -174,13 +179,17 @@ public class EdgeBackGestureHandler implements DisplayListener, 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 @@ public class EdgeBackGestureHandler implements DisplayListener, } if (!mIsEnabled) { + mGestureNavigationSettingsObserver.unregister(); mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); mPluginManager.removePluginListener(this); @@ -248,6 +258,7 @@ public class EdgeBackGestureHandler implements DisplayListener, } } else { + mGestureNavigationSettingsObserver.register(); updateDisplaySize(); mContext.getSystemService(DisplayManager.class).registerDisplayListener(this, mContext.getMainThreadHandler()); @@ -321,7 +332,8 @@ public class EdgeBackGestureHandler implements DisplayListener, 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 @@ public class EdgeBackGestureHandler implements DisplayListener, 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 @@ public class EdgeBackGestureHandler implements DisplayListener, 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/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index ee313001bfc4..90bc075b399d 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 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL 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/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index b119f0b1f1e2..31266db9e144 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 class NavigationBarFragment extends LifecycleFragment implements Callback 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 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback @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 43ac4cf732aa..d24ccf343a3a 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.Context; 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 class NavigationModeController implements Dumpable { 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 @@ public class NavigationModeController implements Dumpable { } } + 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 5d8044f37c38..000000000000 --- 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/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 1359f74d0b3a..e25c14c86fd5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -40,6 +40,7 @@ import com.android.systemui.R; 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 +48,6 @@ public class PhoneStatusBarView extends PanelBar { 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 +64,14 @@ public class PhoneStatusBarView extends PanelBar { } }; 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 +108,7 @@ public class PhoneStatusBarView extends PanelBar { 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 +125,7 @@ public class PhoneStatusBarView extends PanelBar { super.onConfigurationChanged(newConfig); // May trigger cutout space layout-ing - if (updateOrientationAndCutout(newConfig.orientation)) { + if (updateOrientationAndCutout()) { updateLayoutForCutout(); requestLayout(); } @@ -132,7 +133,7 @@ public class PhoneStatusBarView extends PanelBar { @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { - if (updateOrientationAndCutout(mLastOrientation)) { + if (updateOrientationAndCutout()) { updateLayoutForCutout(); requestLayout(); } @@ -140,17 +141,14 @@ public class PhoneStatusBarView extends PanelBar { } /** - * - * @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)) { @@ -293,17 +291,16 @@ public class PhoneStatusBarView extends PanelBar { 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 d1ff32d9304e..c5901398a54b 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.Dumpable; 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo /** * 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters, mDockManager); states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard); + states[i].setDefaultScrimAlpha(mDefaultScrimAlpha); } mScrimBehind.setDefaultFocusHighlightEnabled(false); @@ -483,7 +488,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo 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 c23fd0a3d5d6..ade642c5ea9a 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 @@ public enum ScrimState { BOUNCER { @Override public void prepare(ScrimState previousState) { - mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA; + mBehindAlpha = mDefaultScrimAlpha; mFrontAlpha = 0f; mBubbleAlpha = 0f; } @@ -106,7 +106,7 @@ public enum ScrimState { public void prepare(ScrimState previousState) { mBehindAlpha = 0; mBubbleAlpha = 0f; - mFrontAlpha = ScrimController.BUSY_SCRIM_ALPHA; + mFrontAlpha = mDefaultScrimAlpha; } }, @@ -233,9 +233,9 @@ public enum ScrimState { 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 @@ public enum ScrimState { float mBubbleAlpha; float mScrimBehindAlphaKeyguard; + float mDefaultScrimAlpha; ScrimView mScrimInFront; ScrimView mScrimBehind; ScrimView mScrimForBubble; @@ -341,6 +342,10 @@ public enum ScrimState { 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 e6f24c22b34f..d343090900a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2602,7 +2602,7 @@ public class StatusBar extends SystemUI implements DemoMode, } 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/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 22bf513272f4..760a6d6a71c3 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.View; import android.view.WindowInsets; import android.widget.FrameLayout; +import com.android.systemui.util.leak.RotationUtils; + /** * Status bar view. */ @@ -52,17 +54,13 @@ public class StatusBarWindowView extends FrameLayout { @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 @@ public class StatusBarWindowView extends FrameLayout { 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 @@ public class StatusBarWindowView extends FrameLayout { 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/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index cebcf760a990..54e8e72f4364 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 @@ public class MobileSignalController extends SignalController< } boolean isDataDisabled() { - return !mPhone.isDataConnectionEnabled(); + return !mPhone.isDataConnectionAllowed(); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 2f1b160ffd4b..54118390325c 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 @@ import javax.inject.Singleton; 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 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if ((types & WindowInsets.Type.ime()) == 0) { return; } + if (DEBUG) Slog.d(TAG, "Got showInsets for ime"); startAnimation(true /* show */); } @@ -217,6 +220,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if ((types & WindowInsets.Type.ime()) == 0) { return; } + if (DEBUG) Slog.d(TAG, "Got hideInsets for ime"); startAnimation(false /* show */); } @@ -241,6 +245,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged 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 class DisplayImeController implements DisplayController.OnDisplaysChanged 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 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } @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 ccb86999674c..381ccdb50386 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 @@ public class SystemWindows { 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); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 1b34b3d85c09..b6537bf8c615 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 android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CO 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 static org.mockito.Mockito.when; 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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,12 +166,70 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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(); + + 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()); + } + + @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()); @@ -179,6 +242,11 @@ public class ScreenDecorationsTest extends SysuiTestCase { // 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 @@ -193,6 +261,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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 @@ public class ScreenDecorationsTest extends SysuiTestCase { 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/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java index c6c7b87da544..1db8e4c3d73e 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 @@ public class AuthContainerViewTest extends SysuiTestCase { 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 @@ public class AuthContainerViewTest extends SysuiTestCase { 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 @@ public class AuthContainerViewTest extends SysuiTestCase { 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 @@ public class AuthContainerViewTest extends SysuiTestCase { 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 @@ public class AuthContainerViewTest extends SysuiTestCase { @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 65399bfb901e..fc1ddf74a448 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 com.android.systemui.statusbar.CommandQueue; 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 @@ public class AuthControllerTest extends SysuiTestCase { @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 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { biometricModality, true /* requireConfirmation */, 0 /* userId */, - "testPackage"); + "testPackage", + 0 /* operationId */); } private Bundle createTestDialogBundle(int authenticators) { @@ -453,6 +490,13 @@ public class AuthControllerTest extends SysuiTestCase { 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 @@ public class AuthControllerTest extends SysuiTestCase { @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/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java index f40fc94763ab..866dfdc9c7e4 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 @@ public class BubbleDataTest extends SysuiTestCase { 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 @@ public class BubbleDataTest extends SysuiTestCase { 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/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java index e3187cb9a6c5..b1ac022dbe9c 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 org.mockito.Mock; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.IntSupplier; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -59,7 +60,13 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase @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 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase */ 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/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 9117ea8f9fc2..f535351f0f13 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 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @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 8320b058208c..6871aad0ea98 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.SysuiTestCase; 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { 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 a2ab78483b68..b863f143056f 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 @@ public class PipAnimationControllerTest extends SysuiTestCase { @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 b12db2b44e3d..0bf0f04d2d43 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.graphics.Rect; 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 @@ public class PipBoundsHandlerTest extends SysuiTestCase { 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 @@ public class PipBoundsHandlerTest extends SysuiTestCase { }; 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 @@ public class PipBoundsHandlerTest extends SysuiTestCase { }; 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 @@ public class PipBoundsHandlerTest extends SysuiTestCase { 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 @@ public class PipBoundsHandlerTest extends SysuiTestCase { } @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 class PipBoundsHandlerTest extends SysuiTestCase { 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 class PipBoundsHandlerTest extends SysuiTestCase { 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 class PipBoundsHandlerTest extends SysuiTestCase { 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 class PipBoundsHandlerTest extends SysuiTestCase { 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/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java index 616399afe7a4..ac304210d416 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 androidx.test.filters.SmallTest; 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; @@ -85,6 +86,8 @@ public class QSPanelTest extends SysuiTestCase { private NotificationMediaManager mNotificationMediaManager; @Mock private Executor mBackgroundExecutor; + @Mock + private LocalBluetoothManager mLocalBluetoothManager; @Before public void setup() throws Exception { @@ -94,7 +97,8 @@ public class QSPanelTest extends SysuiTestCase { mTestableLooper.runWithLooper(() -> { mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher, - mQSLogger, mNotificationMediaManager, mBackgroundExecutor); + mQSLogger, mNotificationMediaManager, 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/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 7c6da630b3bf..cffcabb55e14 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 class CommandQueueTest extends SysuiTestCase { 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/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 408dfc0e1dee..23f263746fb3 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.DejankUtils; 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 @@ public class ScrimControllerTest extends SysuiTestCase { private SysuiColorExtractor mSysuiColorExtractor; @Mock private DockManager mDockManager; + @Mock + private BlurUtils mBlurUtils; private static class AnimatorListener implements Animator.AnimatorListener { @@ -215,7 +218,7 @@ public class ScrimControllerTest extends SysuiTestCase { 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/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 831925f8bb26..cddbb9f508c5 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 @@ public class NetworkControllerBaseTest extends SysuiTestCase { 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); 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 3eb0c44491fc..d8b6aac8fc61 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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @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/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index a18f5da60cad..fd6f171487a9 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 @@ import android.os.ResultReceiver; * @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/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml index 6f692c8021c0..b9c5f1ded3d5 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/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index e73f9ce4f7c0..53afa6e283d9 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.logging.nano.MetricsProto.MetricsEvent; 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 @@ final class RemoteAugmentedAutofillService 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 826500952e60..9693535e2286 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -3382,6 +3382,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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; @@ -3405,7 +3407,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } 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 ee59d89b7f15..0ca9dd92877f 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 @@ public final class InlineSuggestionFactory { @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/core/Android.bp b/services/core/Android.bp index 942d5633e381..9e19ad2aecf4 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -98,9 +98,9 @@ java_library_static { "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 @@ java_library_static { "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 dadcd4e03f89..9fddafbe023b 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -991,4 +991,9 @@ public abstract class PackageManagerInternal { * @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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 7287a44600fa..3a3358c4c059 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2083,9 +2083,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void enforceNetworkFactoryPermission() { - mContext.enforceCallingOrSelfPermission( + enforceAnyPermissionOf( android.Manifest.permission.NETWORK_FACTORY, - "ConnectivityService"); + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } private boolean checkSettingsPermission() { @@ -7803,12 +7803,15 @@ public class ConnectivityService extends IConnectivityManager.Stub 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); @@ -7824,13 +7827,16 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + 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<>(); diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index 318a03074492..191a9bc7651d 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.AvbPublicKey; 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 @@ import java.io.File; * 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 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements 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 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements @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 acd40395f611..d814b9c9f1fa 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -200,7 +200,7 @@ public class LocationManagerService extends ILocationManager.Stub { // 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 b464422e9e3d..52a1b5a62941 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -354,6 +354,10 @@ public class PackageWatchdog { */ 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()) { diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 80036bb4b4fa..808d322020cb 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -99,6 +99,8 @@ public class RescueParty { 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 @@ public class RescueParty { 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 9082dca1022c..db3db0c80e3c 100644 --- a/services/core/java/com/android/server/SensorNotificationService.java +++ b/services/core/java/com/android/server/SensorNotificationService.java @@ -48,7 +48,7 @@ public class SensorNotificationService extends SystemService 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 @@ public class SensorNotificationService extends SystemService 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 b7d050a25484..2c220d3f8861 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.HexDump; 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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub @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 @@ class StorageManagerService extends IStorageManager.Stub 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); + @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 { - 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); - } + 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); } @@ -4309,7 +4360,8 @@ class StorageManagerService extends IStorageManager.Stub 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,41 @@ class StorageManagerService extends IStorageManager.Stub } /** - * 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); - - // 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()); - } + 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()); } - } catch (ServiceManager.ServiceNotFoundException | RemoteException e) { - Slog.e(TAG, "Unable to create obb and data directories for " + processName, e); } + } catch (ServiceManager.ServiceNotFoundException | RemoteException e) { + Slog.e(TAG, "Unable to create obb and data directories for " + processName,e); + return false; } + return true; } @Override diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index eace86b7b976..12a1a9516329 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -1318,9 +1318,9 @@ final class UiModeManagerService extends SystemService { 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 cfc8b459b8a5..0ebb5bba539d 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } } - /** 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 @@ public final class ActiveServices { 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 f64272bf08d1..4485af1ae1f6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17538,8 +17538,11 @@ public class ActivityManagerService extends IActivityManager.Stub 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 +18717,11 @@ public class ActivityManagerService extends IActivityManager.Stub } @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; diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index b584ea5b7a25..2b1534b1c96f 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.ActivityManager; 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 @@ public final class OomAdjuster { * 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 @@ public final class OomAdjuster { 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 6b165139aefd..57bd42bfd05e 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.ApplicationExitInfo.SubReason; 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.IntentFilter; 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.provider.DeviceConfig; 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.nio.ByteBuffer; 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 @@ public final class ProcessList { "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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } } + /** + * 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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. @@ -2128,8 +2193,10 @@ public final class ProcessList { } final Map<String, Pair<String, Long>> pkgDataInfoMap; + boolean bindMountAppStorageDirs = false; if (shouldIsolateAppData(app)) { + bindMountAppStorageDirs = mVoldAppDataIsolationEnabled; // Get all packages belongs to the same shared uid. sharedPackages is empty array // if it doesn't have shared uid. final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked(); @@ -2138,11 +2205,17 @@ public final class ProcessList { pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0 ? new String[]{app.info.packageName} : sharedPackages, uid); + int userId = UserHandle.getUserId(uid); if (mVoldAppDataIsolationEnabled) { StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); - storageManagerInternal.prepareObbDirs(UserHandle.getUserId(uid), - pkgDataInfoMap.keySet(), app.processName); + StorageManagerInternal.class); + 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 +2236,7 @@ public final class ProcessList { 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 +2244,7 @@ public final class ProcessList { 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 +2703,7 @@ public final class ProcessList { 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 c0298110580e..e7f66bb484e0 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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 0a8e70c7d772..bac7565adfa5 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 @@ public class SettingsToPropertiesMapper { @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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 87e1dbc2391c..2e2241f5deea 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -20,7 +20,7 @@ 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_SWITCHED_OP; -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; @@ -41,6 +41,7 @@ import static android.app.AppOpsManager.OP_PLAY_AUDIO; 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,10 +78,10 @@ import android.app.ActivityManager; 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; @@ -97,7 +98,7 @@ import android.content.pm.PackageManager; 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; @@ -244,9 +245,15 @@ public class AppOpsService extends IAppOpsService.Stub { 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; @@ -371,14 +378,14 @@ public class AppOpsService extends IAppOpsService.Stub { } 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); } } @@ -549,7 +556,7 @@ public class AppOpsService extends IAppOpsService.Stub { // 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; @@ -557,15 +564,25 @@ public class AppOpsService extends IAppOpsService.Stub { 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: @@ -580,15 +597,24 @@ public class AppOpsService extends IAppOpsService.Stub { 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: @@ -671,8 +697,8 @@ public class AppOpsService extends IAppOpsService.Stub { */ @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; @@ -776,8 +802,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private final class FeatureOp { - public final @Nullable String featureId; + private final class AttributedOp { + public final @Nullable String tag; public final @NonNull Op parent; /** @@ -804,8 +830,8 @@ public class AppOpsService extends IAppOpsService.Stub { @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; } @@ -814,18 +840,18 @@ public class AppOpsService extends IAppOpsService.Stub { * * @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); } /** @@ -835,12 +861,12 @@ public class AppOpsService extends IAppOpsService.Stub { * @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); @@ -851,7 +877,7 @@ public class AppOpsService extends IAppOpsService.Stub { OpEventProxyInfo proxyInfo = null; if (proxyUid != Process.INVALID_UID) { proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName, - proxyFeatureId); + proxyAttributionTag); } NoteOpEvent existingEvent = mAccessEvents.get(key); @@ -872,7 +898,7 @@ public class AppOpsService extends IAppOpsService.Stub { rejected(System.currentTimeMillis(), uidState, flags); mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, parent.packageName, - featureId, uidState, flags); + tag, uidState, flags); } /** @@ -938,7 +964,7 @@ public class AppOpsService extends IAppOpsService.Stub { // 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); } /** @@ -978,7 +1004,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -986,7 +1012,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -1077,14 +1103,14 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * 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"); @@ -1128,7 +1154,7 @@ public class AppOpsService extends IAppOpsService.Stub { return clone; } - @NonNull OpFeatureEntry createFeatureEntryLocked() { + @NonNull AttributedOpEntry createAttributedOpEntryLocked() { LongSparseArray<NoteOpEvent> accessEvents = deepClone(mAccessEvents); // Add in progress events as access events @@ -1152,7 +1178,7 @@ public class AppOpsService extends IAppOpsService.Stub { LongSparseArray<NoteOpEvent> rejectEvents = deepClone(mRejectEvents); - return new OpFeatureEntry(parent.op, isRunning(), accessEvents, rejectEvents); + return new AttributedOpEntry(parent.op, isRunning(), accessEvents, rejectEvents); } } @@ -1164,8 +1190,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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; @@ -1183,58 +1209,59 @@ public class AppOpsService extends IAppOpsService.Stub { 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; } } @@ -1407,10 +1434,11 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * 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); } @@ -1492,20 +1520,21 @@ public class AppOpsService extends IAppOpsService.Stub { 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); } } } @@ -1523,27 +1552,30 @@ public class AppOpsService extends IAppOpsService.Stub { // 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(); } @@ -1651,17 +1683,14 @@ public class AppOpsService extends IAppOpsService.Stub { } }, 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); @@ -1743,12 +1772,13 @@ public class AppOpsService extends IAppOpsService.Stub { 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)); } } } @@ -1828,11 +1858,13 @@ public class AppOpsService extends IAppOpsService.Stub { 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); } } } @@ -1978,9 +2010,9 @@ public class AppOpsService extends IAppOpsService.Stub { /** * 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 { @@ -1993,8 +2025,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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) { @@ -2004,17 +2036,18 @@ public class AppOpsService extends IAppOpsService.Stub { } 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"); @@ -2035,15 +2068,15 @@ public class AppOpsService extends IAppOpsService.Stub { // 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"); @@ -2055,7 +2088,7 @@ public class AppOpsService extends IAppOpsService.Stub { // 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()); } @@ -2089,9 +2122,9 @@ public class AppOpsService extends IAppOpsService.Stub { } 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); @@ -2610,8 +2643,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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); } } @@ -2895,8 +2928,8 @@ public class AppOpsService extends IAppOpsService.Stub { @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); @@ -2912,7 +2945,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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; @@ -2925,27 +2958,27 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -2953,25 +2986,26 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -2980,17 +3014,17 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -3002,7 +3036,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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; } @@ -3014,26 +3048,31 @@ public class AppOpsService extends IAppOpsService.Stub { 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; @@ -3131,11 +3170,11 @@ public class AppOpsService extends IAppOpsService.Stub { * @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(); @@ -3147,10 +3186,10 @@ public class AppOpsService extends IAppOpsService.Stub { 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) { @@ -3161,7 +3200,7 @@ public class AppOpsService extends IAppOpsService.Stub { } catch (RemoteException e) { Slog.e(TAG, "Could not forward noteOp of " + opCode + " to " + packageName - + "/" + uid + "(" + featureId + ")", e); + + "/" + uid + "(" + attributionTag + ")", e); } }); } @@ -3263,7 +3302,7 @@ public class AppOpsService extends IAppOpsService.Stub { @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); @@ -3274,14 +3313,14 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ -3292,7 +3331,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ -3305,7 +3344,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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 { @@ -3317,21 +3356,21 @@ public class AppOpsService extends IAppOpsService.Stub { 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; @@ -3339,7 +3378,7 @@ public class AppOpsService extends IAppOpsService.Stub { @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); @@ -3349,27 +3388,31 @@ public class AppOpsService extends IAppOpsService.Stub { 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)); } } } @@ -3636,25 +3679,25 @@ public class AppOpsService extends IAppOpsService.Stub { * * @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; } } @@ -3666,17 +3709,17 @@ public class AppOpsService extends IAppOpsService.Stub { 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; } } } @@ -3686,8 +3729,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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) { @@ -3699,9 +3742,9 @@ public class AppOpsService extends IAppOpsService.Stub { + " 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 { @@ -3716,13 +3759,13 @@ public class AppOpsService extends IAppOpsService.Stub { * * @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) { @@ -3750,8 +3793,8 @@ public class AppOpsService extends IAppOpsService.Stub { ops.bypass = bypass; } - if (featureId != null) { - ops.knownFeatureIds.add(featureId); + if (attributionTag != null) { + ops.knownAttributionTags.add(attributionTag); } } @@ -3780,15 +3823,15 @@ public class AppOpsService extends IAppOpsService.Stub { * @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; } @@ -4045,9 +4088,9 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -4058,14 +4101,14 @@ public class AppOpsService extends IAppOpsService.Stub { 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); } } @@ -4091,7 +4134,7 @@ public class AppOpsService extends IAppOpsService.Stub { } 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()); @@ -4205,11 +4248,11 @@ public class AppOpsService extends IAppOpsService.Stub { 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++) { @@ -4218,14 +4261,14 @@ public class AppOpsService extends IAppOpsService.Stub { final int uidState = AppOpsManager.extractUidStateFromKey(key); final int flags = AppOpsManager.extractFlagsFromKey(key); - final long accessTime = feature.getLastAccessTime(uidState, - uidState, flags); - final long rejectTime = feature.getLastRejectTime(uidState, + final long accessTime = attribution.getLastAccessTime(uidState, uidState, flags); - final long accessDuration = feature.getLastDuration(uidState, + final long rejectTime = attribution.getLastRejectTime(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 @@ -4234,17 +4277,17 @@ public class AppOpsService extends IAppOpsService.Stub { } 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) { @@ -4259,8 +4302,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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)); @@ -4294,7 +4337,7 @@ public class AppOpsService extends IAppOpsService.Stub { int userId = UserHandle.USER_SYSTEM; String packageName; - String featureId; + String attributionTag; String opStr; String modeStr; int op; @@ -4396,8 +4439,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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; @@ -4492,13 +4535,16 @@ public class AppOpsService extends IAppOpsService.Stub { 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."); @@ -4512,8 +4558,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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) { @@ -4602,7 +4648,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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( @@ -4622,29 +4668,30 @@ public class AppOpsService extends IAppOpsService.Stub { 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); } } } @@ -4752,7 +4799,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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; @@ -4766,8 +4813,8 @@ public class AppOpsService extends IAppOpsService.Stub { } 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; } @@ -4792,35 +4839,35 @@ public class AppOpsService extends IAppOpsService.Stub { 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(); @@ -4837,11 +4884,11 @@ public class AppOpsService extends IAppOpsService.Stub { 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(); } @@ -4865,8 +4912,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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(); @@ -4887,21 +4934,21 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -4924,7 +4971,7 @@ public class AppOpsService extends IAppOpsService.Stub { int dumpOp = OP_NONE; String dumpPackage = null; - String dumpFeatureId = null; + String dumpAttributionTag = null; int dumpUid = Process.INVALID_UID; int dumpMode = -1; boolean dumpWatchers = false; @@ -4971,14 +5018,14 @@ public class AppOpsService extends IAppOpsService.Stub { } 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) { @@ -5317,8 +5364,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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, " "); } } } @@ -5417,7 +5464,7 @@ public class AppOpsService extends IAppOpsService.Stub { // Must not hold the appops lock if (dumpHistory && !dumpWatchers) { - mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpFeatureId, dumpOp, + mHistoricalRegistry.dump(" ", pw, dumpUid, dumpPackage, dumpAttributionTag, dumpOp, dumpFilter); } } @@ -5522,7 +5569,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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) { @@ -5583,7 +5630,7 @@ public class AppOpsService extends IAppOpsService.Stub { * 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 @@ -5594,7 +5641,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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()); @@ -5605,7 +5652,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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()); @@ -5618,17 +5665,18 @@ public class AppOpsService extends IAppOpsService.Stub { * @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); } /** @@ -5636,7 +5684,7 @@ public class AppOpsService extends IAppOpsService.Stub { * 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); @@ -5653,7 +5701,7 @@ public class AppOpsService extends IAppOpsService.Stub { mMessagesCollectedCount += 1.0f; if (ThreadLocalRandom.current().nextFloat() <= 1.0f / mMessagesCollectedCount) { mCollectedRuntimePermissionMessage = new RuntimeAppOpAccessMessage(uid, opCode, - packageName, featureId, message, mSamplingStrategy); + packageName, attributionTag, message, mSamplingStrategy); } return; } @@ -5674,11 +5722,16 @@ public class AppOpsService extends IAppOpsService.Stub { /** * 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; @@ -5740,6 +5793,10 @@ public class AppOpsService extends IAppOpsService.Stub { } } 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 cd450d421b0a..ed4506902f0a 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 static android.app.AppOpsManager.FILTER_BY_UID; 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 @@ final class HistoricalRegistry { } 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { } 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { } } - 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 @@ final class HistoricalRegistry { || 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 @@ final class HistoricalRegistry { - 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 @@ final class HistoricalRegistry { } 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 @@ final class HistoricalRegistry { } 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 @@ final class HistoricalRegistry { } 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 @@ final class HistoricalRegistry { } getUpdatedPendingHistoricalOpsMLocked( System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName, - featureId, uidState, flags, increment); + attributionTag, uidState, flags, increment); } } } @@ -728,7 +727,7 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { @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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { } } - 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 class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { // 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 @@ final class HistoricalRegistry { // 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 class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 class HistoricalRegistry { } 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { } 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { } 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { @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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { } @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 ecdafb0a7a79..e7c09baf3aeb 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.BiometricAuthenticator.TYPE_NONE; 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 @@ public class BiometricService extends SystemService { } case MSG_ON_DISMISSED: { - handleOnDismissed(msg.arg1); + handleOnDismissed(msg.arg1, (byte[]) msg.obj); break; } @@ -611,8 +612,12 @@ public class BiometricService extends SystemService { } @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 @@ public class BiometricService extends SystemService { 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 @@ public class BiometricService extends SystemService { } } - 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 @@ public class BiometricService extends SystemService { 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 @@ public class BiometricService extends SystemService { 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 @@ public class BiometricService extends SystemService { 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 7bdeb5969f1f..2e9818d15963 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 @@ public final class CompatChange extends CompatibilityChangeInfo { 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 b2ea311bf999..aeaa1fedf9e3 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 @@ final class CompatConfig { 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 @@ final class CompatConfig { 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/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 7c3cab17704f..20ffd9f51d6e 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 class Vpn { 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 class Vpn { 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 @@ public class Vpn { } catch (Exception e) { Log.i(TAG, "Aborting", e); updateState(DetailedState.FAILED, e.getMessage()); - exit(); + exitVpnRunner(); } } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 6da0de13f623..44ab43828c2d 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.Binder; 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 com.android.server.pm.parsing.pkg.ParsedPackage; 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.HashSet; 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 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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 b158388281d8..682b104f6d7a 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 @@ import android.os.Handler; * @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 @@ public abstract class CountryDetectorBase { 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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 69a5b35a5b12..7b1003f39175 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -931,6 +931,8 @@ public class NotificationManagerService extends SystemService { .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 +972,8 @@ public class NotificationManagerService extends SystemService { 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 +1007,8 @@ public class NotificationManagerService extends SystemService { 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 +1019,8 @@ public class NotificationManagerService extends SystemService { @Override public void onPanelHidden() { MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL); + mNotificationRecordLogger.log( + NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_CLOSE); EventLogTags.writeNotificationPanelHidden(); mAssistants.onPanelHidden(); } @@ -1103,6 +1110,10 @@ public class NotificationManagerService extends SystemService { 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 +1135,9 @@ public class NotificationManagerService extends SystemService { 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 +1180,9 @@ public class NotificationManagerService extends SystemService { 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( @@ -1310,6 +1327,9 @@ public class NotificationManagerService extends SystemService { MetricsEvent.NOTIFICATION_SMART_REPLY_EDIT_BEFORE_SENDING, r.getEditChoicesBeforeSending() ? 1 : 0); mMetricsLogger.write(logMaker); + mNotificationRecordLogger.log( + NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLY_VISIBLE, + r); } } @@ -6150,6 +6170,9 @@ public class NotificationManagerService extends SystemService { 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 +6302,8 @@ public class NotificationManagerService extends SystemService { 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); diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index f4ee4616df27..6c833f966d36 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.ArrayList; 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 @@ public interface NotificationRecordLogger { * @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 @@ public interface NotificationRecordLogger { * @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 @@ public interface NotificationRecordLogger { @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 interface NotificationRecordLogger { 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 9fcac257d328..494ff314a8aa 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 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { } @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/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index 6c2d77cac6d3..8349632a3743 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 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @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 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } synchronized (mLock) { startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd, - bugreportMode, listener); + bugreportMode, listener, isScreenshotRequested); } } @@ -145,7 +145,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @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 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { 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/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 483f83efd508..28d7c13d5b13 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; @@ -166,6 +167,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 +314,41 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @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; @@ -516,8 +552,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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"); + } } } @@ -623,6 +663,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } info.grantedRuntimePermissions = params.grantedRuntimePermissions; info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions; + info.autoRevokePermissionsMode = params.autoRevokePermissionsMode; info.installFlags = params.installFlags; info.isMultiPackage = params.isMultiPackage; info.isStaged = params.isStaged; @@ -750,9 +791,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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; } @@ -2444,7 +2495,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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); + } } } @@ -2462,7 +2516,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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); + } } } @@ -2482,7 +2539,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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; @@ -2889,6 +2947,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + 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"); @@ -2969,6 +3034,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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); @@ -2995,7 +3061,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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()); @@ -3112,6 +3180,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // 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(); @@ -3128,6 +3197,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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)); } @@ -3150,6 +3222,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 799ce65669db..24ebd32f2e2d 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.READ_EXTERNAL_STORAGE; 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; @@ -1654,12 +1657,13 @@ public class PackageManagerService extends IPackageManager.Stub && 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 @@ -1669,7 +1673,8 @@ public class PackageManagerService extends IPackageManager.Stub 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); } @@ -1998,6 +2003,7 @@ public class PackageManagerService extends IPackageManager.Stub 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; @@ -2018,6 +2024,11 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ -11514,8 +11525,8 @@ public class PackageManagerService extends IPackageManager.Stub "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"); } @@ -14295,6 +14306,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageAbiOverride; final String[] grantedRuntimePermissions; final List<String> whitelistedRestrictedPermissions; + final int autoRevokePermissionsMode; final VerificationInfo verificationInfo; final PackageParser.SigningDetails signingDetails; final int installReason; @@ -14309,6 +14321,7 @@ public class PackageManagerService extends IPackageManager.Stub 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); @@ -14322,6 +14335,7 @@ public class PackageManagerService extends IPackageManager.Stub this.packageAbiOverride = packageAbiOverride; this.grantedRuntimePermissions = grantedPermissions; this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; + this.autoRevokePermissionsMode = autoRevokePermissionsMode; this.signingDetails = signingDetails; this.installReason = installReason; this.requiredInstalledVersionCode = requiredInstalledVersionCode; @@ -14358,6 +14372,7 @@ public class PackageManagerService extends IPackageManager.Stub packageAbiOverride = sessionParams.abiOverride; grantedRuntimePermissions = sessionParams.grantedRuntimePermissions; whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions; + autoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode; signingDetails = activeInstallSession.getSigningDetails(); requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode; forceQueryableOverride = sessionParams.forceQueryableOverride; @@ -14726,9 +14741,8 @@ public class PackageManagerService extends IPackageManager.Stub 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); @@ -14794,9 +14808,9 @@ public class PackageManagerService extends IPackageManager.Stub } } - 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 @@ -14954,6 +14968,7 @@ public class PackageManagerService extends IPackageManager.Stub 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; @@ -14973,6 +14988,7 @@ public class PackageManagerService extends IPackageManager.Stub 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) { @@ -14987,6 +15003,7 @@ public class PackageManagerService extends IPackageManager.Stub this.abiOverride = abiOverride; this.installGrantPermissions = installGrantPermissions; this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; + this.autoRevokePermissionsMode = autoRevokePermissionsMode; this.traceMethod = traceMethod; this.traceCookie = traceCookie; this.signingDetails = signingDetails; @@ -15002,6 +15019,7 @@ public class PackageManagerService extends IPackageManager.Stub 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); @@ -15093,7 +15111,7 @@ public class PackageManagerService extends IPackageManager.Stub /** 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); @@ -22469,7 +22487,8 @@ public class PackageManagerService extends IPackageManager.Stub 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)); @@ -24173,6 +24192,12 @@ public class PackageManagerService extends IPackageManager.Stub } mAppsFilter.getFeatureConfig().enableLogging(pkg.appId, enable); } + + @Override + public boolean isSystemPackage(@NonNull String packageName) { + return packageName.equals( + PackageManagerService.this.ensureSystemPackageName(packageName)); + } } @GuardedBy("mLock") @@ -24361,6 +24386,24 @@ public class PackageManagerService extends IPackageManager.Stub } @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 be17dd8989a3..8a9f1b3afd02 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { } } 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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index df3c83aec85a..1c2fcc15b399 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1300,14 +1300,15 @@ public class UserManagerService extends IUserManager.Stub { @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 +1321,8 @@ public class UserManagerService extends IUserManager.Stub { @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 +1335,7 @@ public class UserManagerService extends IUserManager.Stub { @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 +1346,7 @@ public class UserManagerService extends IUserManager.Stub { @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 +1357,7 @@ public class UserManagerService extends IUserManager.Stub { @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 +1369,7 @@ public class UserManagerService extends IUserManager.Stub { @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 +1378,7 @@ public class UserManagerService extends IUserManager.Stub { @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 +1387,20 @@ public class UserManagerService extends IUserManager.Stub { @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 +1440,7 @@ public class UserManagerService extends IUserManager.Stub { } } - 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 +1469,7 @@ public class UserManagerService extends IUserManager.Stub { @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 +1825,7 @@ public class UserManagerService extends IUserManager.Stub { /** @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 +1950,7 @@ public class UserManagerService extends IUserManager.Stub { */ @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 d3f668c5f4d3..0e294f707985 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 @@ public class PackageInfoUtils { 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 792957935870..46b08df1a52e 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.PackageParser; 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 @@ public interface AndroidPackage extends PkgAppInfo, PkgPackageInfo, ParsingPacka 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 85da5593223b..46f121d8c09d 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 @@ package com.android.server.pm.permission; 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.NonNull; 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 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** 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 @@ public class PermissionManagerService extends IPermissionManager.Stub { 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 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @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 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + @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; @@ -4347,6 +4455,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { 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 32ef2cee5685..356d0abc1d19 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 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager /** 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 d589353cf3a0..161f30449a52 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.AppOpsManager; 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 @@ public final class PermissionPolicyService extends SystemService { } 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 @@ public final class PermissionPolicyService extends SystemService { * {@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 1b5cc6a248e3..1e1256547f2c 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } 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/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index beba106ca0a6..8b6e9a46ca91 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.os.UserHandle; 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 com.android.server.power.batterysaver.BatterySavingStats.InteractiveState import java.util.ArrayList; import java.util.Objects; +import java.util.Optional; /** * Responsible for battery saver mode transition logic. @@ -112,6 +116,14 @@ public class BatterySaverController implements BatterySaverPolicyListener { */ 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 @@ public class BatterySaverController implements BatterySaverPolicyListener { 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 @@ public class BatterySaverController implements BatterySaverPolicyListener { } } + 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/security/TEST_MAPPING b/services/core/java/com/android/server/security/TEST_MAPPING new file mode 100644 index 000000000000..9a5e90e8681f --- /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 856a40f3ef12..2b793c894eb0 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 @@ abstract public class VerityUtils { /** 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 98579af5835b..fd275d83ba56 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 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { registerAppsOnExternalStorageInfo(); registerFaceSettings(); registerAppOps(); - registerAppFeaturesOps(); + registerAttributedAppOps(); registerRuntimeAppOpAccessMessage(); registerNotificationRemoteViews(); registerDangerousPermissionState(); @@ -2888,6 +2888,44 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { ); } - 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 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { e.setAtomId(atomTag); e.writeInt(uid); e.writeString(packageName); - if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { - e.writeString(feature); - } - if (atomTag == FrameworkStatsLog.APP_FEATURES_OPS) { - e.writeString(op.getOpName()); - } else { - e.writeInt(op.getOpCode()); + if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { + e.writeString(attributionTag); } + 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 @@ public class StatsPullAtomService extends SystemService { 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 @@ public class StatsPullAtomService extends SystemService { 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 84cd4cfd67be..78ef68c09988 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @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 816452679c45..74a6383bc01c 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.Slog; 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Nullable TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException { Objects.requireNonNull(event); + Objects.requireNonNull(event.getSystemTextClassifierMetadata()); handleRequest( - event.getUserId(), - /* callingPackageName= */ null, + event.getSystemTextClassifierMetadata(), + /* verifyCallingPackage= */ false, /* attemptToBind= */ false, - event.getUseDefaultTextClassifier(), service -> service.onSelectionEvent(sessionId, event), "onSelectionEvent", NO_OP_CALLBACK); @@ -246,18 +247,14 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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= */ false, /* attemptToBind= */ false, - useDefaultTextClassifier, service -> service.onTextClassifierEvent(sessionId, event), "onTextClassifierEvent", NO_OP_CALLBACK); @@ -269,12 +266,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi } 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 class TextClassificationManagerService extends ITextClassifierServi 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 752aa6683a88..a2fe5fcde8c2 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 @@ import java.io.IOException; 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 @@ public final class UinputBridge { 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 @@ public final class UinputBridge { 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 @@ public final class UinputBridge { 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 04d551d0f84e..bd63b2daad83 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -16,6 +16,7 @@ package com.android.server.tv.tunerresourcemanager; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -24,6 +25,8 @@ import android.media.tv.tunerresourcemanager.CasSessionRequest; 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; @@ -38,6 +41,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -68,9 +73,31 @@ public class TunerResourceManagerService extends SystemService { 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(); + /** + * 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; + public TunerResourceManagerService(@Nullable Context context) { super(context); } @@ -94,7 +121,7 @@ public class TunerResourceManagerService extends SystemService { 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 +145,7 @@ public class TunerResourceManagerService extends SystemService { @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 +157,7 @@ public class TunerResourceManagerService extends SystemService { @Override public boolean updateClientPriority(int clientId, int priority, int niceValue) { - enforceAccessPermission(); + enforceTrmAccessPermission("updateClientPriority"); synchronized (mLock) { return updateClientPriorityInternal(clientId, priority, niceValue); } @@ -138,7 +165,7 @@ public class TunerResourceManagerService extends SystemService { @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 +176,7 @@ public class TunerResourceManagerService extends SystemService { @Override public void updateCasInfo(int casSystemId, int maxSessionNum) { + enforceTrmAccessPermission("updateCasInfo"); if (DEBUG) { Slog.d(TAG, "updateCasInfo(casSystemId=" + casSystemId @@ -158,6 +186,7 @@ public class TunerResourceManagerService extends SystemService { @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 +196,15 @@ public class TunerResourceManagerService extends SystemService { @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 +213,43 @@ public class TunerResourceManagerService extends SystemService { @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 +258,9 @@ public class TunerResourceManagerService extends SystemService { } @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 +269,34 @@ public class TunerResourceManagerService extends SystemService { @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 +304,8 @@ public class TunerResourceManagerService extends SystemService { @Override public void releaseLnb(int lnbId) { + enforceTunerAccessPermission("releaseLnb"); + enforceTrmAccessPermission("releaseLnb"); if (DEBUG) { Slog.d(TAG, "releaseLnb(lnbId=" + lnbId + ")"); } @@ -230,6 +314,7 @@ public class TunerResourceManagerService extends SystemService { @Override public boolean isHigherPriority( ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) { + enforceTrmAccessPermission("isHigherPriority"); if (DEBUG) { Slog.d(TAG, "isHigherPriority(challengerProfile=" + challengerProfile @@ -337,13 +422,13 @@ public class TunerResourceManagerService extends SystemService { } @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 +464,20 @@ public class TunerResourceManagerService extends SystemService { // Grant frontend when there is unused resource. if (grantingFrontendId > -1) { - frontendId[0] = grantingFrontendId; - updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId()); + frontendHandle[0] = generateResourceHandle( + 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( + TUNER_RESOURCE_TYPE_FRONTEND, inUseLowestPriorityFrId); + reclaimFrontendResource(getFrontendResource( + inUseLowestPriorityFrId).getOwnerClientId()); + updateFrontendClientMappingOnNewGrant(inUseLowestPriorityFrId, request.getClientId()); return true; } @@ -397,6 +485,24 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting + boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { + if (DEBUG) { + Slog.d(TAG, "requestDemux(request=" + request + ")"); + } + demuxHandle[0] = generateResourceHandle(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(TUNER_RESOURCE_TYPE_DESCRAMBLER, 0); + return true; + } + + @VisibleForTesting protected class ResourcesReclaimListenerRecord implements IBinder.DeathRecipient { private final IResourcesReclaimListener mListener; private final int mClientId; @@ -558,8 +664,24 @@ public class TunerResourceManagerService extends SystemService { return mClientProfiles.keySet().contains(clientId); } - private void enforceAccessPermission() { - getContext().enforceCallingOrSelfPermission( - "android.permission.TUNER_RESOURCE_ACCESS", TAG); + private int generateResourceHandle(@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 761fbf8a0622..88a60ddc5a1e 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 @@ public final class TwilightService extends SystemService 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 @@ public final class TwilightService extends SystemService 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/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b6ad24184803..c4545fa23cc8 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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; @@ -6357,33 +6367,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** + * 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // 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 9bad799b68d1..4ebb4236b84f 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.Slog; 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 @@ class ActivityStack extends Task { // 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 @@ class ActivityStack extends Task { 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 @@ class ActivityStack extends Task { } } - 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 diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 98e3d07fc2e4..94821ace2a05 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -6523,6 +6523,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo 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 ba61667cc871..ab96c616e3fd 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -179,6 +179,7 @@ import android.view.accessibility.AccessibilityManager; 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 +260,9 @@ public class DisplayPolicy { @Px private int mBottomGestureAdditionalInset; @Px - private int mSideGestureInset; + private int mLeftGestureInset; + @Px + private int mRightGestureInset; StatusBarManagerInternal getStatusBarManagerInternal() { synchronized (mServiceAcquireLock) { @@ -427,6 +430,8 @@ public class DisplayPolicy { 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) { @@ -651,6 +656,16 @@ public class DisplayPolicy { 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() { @@ -723,7 +738,7 @@ public class DisplayPolicy { } boolean hasSideGestures() { - return mHasNavigationBar && mSideGestureInset > 0; + return mHasNavigationBar && (mLeftGestureInset > 0 || mRightGestureInset > 0); } public boolean navigationBarCanMove() { @@ -1076,11 +1091,12 @@ public class DisplayPolicy { 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; @@ -2819,7 +2835,8 @@ public class DisplayPolicy { } 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); @@ -3953,6 +3970,10 @@ public class DisplayPolicy { 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 01f98888c777..30912e55f908 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.IntArray; 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.SurfaceControl; 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 @@ class InsetsPolicy { InsetsPolicyAnimationControlCallbacks mControlCallbacks; InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) { - super(show); + super(show, true /* useSfVsync */); mFinishCallback = finishCallback; mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); } @@ -360,8 +362,6 @@ class InsetsPolicy { 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 @@ class InsetsPolicy { } @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 @@ class InsetsPolicy { 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/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index 660706e4139b..9371c0eec26f 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 @@ class LaunchParamsPersister { * 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 @@ class LaunchParamsPersister { } void onCleanupUser(int userId) { - mMap.remove(userId); + mLaunchParamsMap.remove(userId); } private void loadLaunchParams(int userId) { @@ -128,7 +138,7 @@ class LaunchParamsPersister { 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 @@ class LaunchParamsPersister { 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 @@ class LaunchParamsPersister { 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 @@ class LaunchParamsPersister { 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 @@ class LaunchParamsPersister { 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 @@ class LaunchParamsPersister { 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 @@ class LaunchParamsPersister { /** 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 @@ class LaunchParamsPersister { } 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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 15a49a791216..ebf1bc988b91 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> 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/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java index 36861aa1f88e..a696dafa7dd3 100644 --- a/services/core/java/com/android/server/wm/SurfaceFreezer.java +++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java @@ -103,7 +103,7 @@ class SurfaceFreezer { */ void unfreeze(SurfaceControl.Transaction t) { if (mSnapshot != null) { - mSnapshot.destroy(t); + mSnapshot.cancelAnimation(t, false /* restarting */); } if (mLeash == null) { return; @@ -212,7 +212,7 @@ class SurfaceFreezer { * @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 +229,6 @@ class SurfaceFreezer { } } 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 5ed903e875c2..e78f2ee47d65 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 @@ class Task extends WindowContainer<WindowContainer> { 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 @@ class Task extends WindowContainer<WindowContainer> { 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 @@ -474,6 +477,7 @@ class Task extends WindowContainer<WindowContainer> { * 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 +1004,8 @@ class Task extends WindowContainer<WindowContainer> { 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 +1935,7 @@ class Task extends WindowContainer<WindowContainer> { // TODO: Should also take care of Pip mode changes here. saveLaunchingStateIfNeeded(); + updateTaskOrganizerState(false /* forceUpdate */); } /** @@ -2120,14 +2127,26 @@ class Task extends WindowContainer<WindowContainer> { 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 +2161,10 @@ class Task extends WindowContainer<WindowContainer> { 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); } @@ -3363,6 +3386,9 @@ class Task extends WindowContainer<WindowContainer> { } else { info.pictureInPictureParams = mPictureInPictureParams; } + info.topActivityInfo = mReuseActivitiesReport.top != null + ? mReuseActivitiesReport.top.info + : null; } /** @@ -3408,6 +3434,9 @@ class Task extends WindowContainer<WindowContainer> { 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 +3618,9 @@ class Task extends WindowContainer<WindowContainer> { } 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 +3778,7 @@ class Task extends WindowContainer<WindowContainer> { String affinity = null; String rootAffinity = null; boolean hasRootAffinity = false; + String windowLayoutAffinity = null; boolean rootHasReset = false; boolean autoRemoveRecents = false; boolean askedCompatMode = false; @@ -3798,6 +3831,9 @@ class Task extends WindowContainer<WindowContainer> { rootAffinity = attrValue; hasRootAffinity = true; break; + case ATTR_WINDOW_LAYOUT_AFFINITY: + windowLayoutAffinity = attrValue; + break; case ATTR_ROOTHASRESET: rootHasReset = Boolean.parseBoolean(attrValue); break; @@ -3953,6 +3989,7 @@ class Task extends WindowContainer<WindowContainer> { 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)); @@ -4018,6 +4055,39 @@ class Task extends WindowContainer<WindowContainer> { // Called on Binder death. void taskOrganizerDied() { mTaskOrganizer = null; + mLastTaskOrganizerWindowingMode = -1; + } + + /** + * 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; } @Override diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index cfb570678e5b..9cbc9ee3f9ee 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -105,7 +105,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub TaskOrganizerState(ITaskOrganizer organizer, int windowingMode, - TaskOrganizerState replacing) { + @Nullable TaskOrganizerState replacing) { mOrganizer = organizer; mDeathRecipient = new DeathRecipient(organizer, windowingMode); try { @@ -203,10 +203,27 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub 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); diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index a60db94ad1e3..0b11dd28e595 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 @@ public class WindowAnimator { mAnimationFrameCallback = frameTimeNs -> { synchronized (mService.mGlobalLock) { mAnimationFrameCallbackScheduled = false; + animate(frameTimeNs); } - animate(frameTimeNs); }; } @@ -115,110 +115,94 @@ public class WindowAnimator { 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) { + if (!mInitialized) { + return; + } - synchronized (mService.mGlobalLock) { - if (!mInitialized) { - return; - } + // Schedule next frame already such that back-pressure happens continuously. + scheduleAnimation(); - // 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); } - 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); + 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); - } - } - - cancelAnimation(); + for (int i = 0; i < numDisplays; i++) { + final int displayId = mDisplayContentsAnimators.keyAt(i); + final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); - if (mService.mWatermark != null) { - mService.mWatermark.drawIfNeeded(); + dc.checkAppWindowsReadyToShow(); + if (accessibilityController != null) { + accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId, + mTransaction); } - - 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 (hasPendingLayoutChanges || doRequest) { - mService.mWindowPlacerLocked.requestTraversal(); + if (mService.mWatermark != null) { + mService.mWatermark.drawIfNeeded(); } - final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN); - if (rootAnimating && !mLastRootAnimating) { + 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"); + } - // 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 hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this); + final boolean doRequest = mBulkUpdateParams != 0 && mService.mRoot.copyAnimToLayoutParams(); + if (hasPendingLayoutChanges || doRequest) { + mService.mWindowPlacerLocked.requestTraversal(); + } - mLastRootAnimating = rootAnimating; + 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); + } - if (mRemoveReplacedWindows) { - mService.mRoot.removeReplacedWindows(); - mRemoveReplacedWindows = false; - } + mLastRootAnimating = rootAnimating; - mService.destroyPreservedSurfaceLocked(); + if (mRemoveReplacedWindows) { + mService.mRoot.removeReplacedWindows(); + mRemoveReplacedWindows = false; + } - executeAfterPrepareSurfacesRunnables(); + mService.destroyPreservedSurfaceLocked(); - if (DEBUG_WINDOW_TRACE) { - Slog.i(TAG, "!!! animate: exit" - + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams) - + " hasPendingLayoutChanges=" + hasPendingLayoutChanges); - } + executeAfterPrepareSurfacesRunnables(); + + 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 e8897e1dbbe2..9a9283257b03 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 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< 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 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< 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,6 +2461,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mWaitingSyncId = -1; } + /** + * Returns true if any of the children elected to participate in the Sync + */ + boolean addChildrenToSyncSet(int localId) { + boolean willSync = false; + + for (int i = 0; i < mChildren.size(); i++) { + final WindowContainer child = mChildren.get(i); + willSync |= mBLASTSyncEngine.addToSyncSet(localId, child); + } + return willSync; + } + boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener, int waitingId) { boolean willSync = false; @@ -2468,16 +2482,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } 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; - } - // 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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ecbbb03a7c94..1a778079668d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1007,9 +1007,7 @@ public class WindowManagerService extends IWindowManager.Stub 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 +1020,8 @@ public class WindowManagerService extends IWindowManager.Stub 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); } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 32eb932ea0ed..f356329cbefb 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 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } 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 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio 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 c44be4d00a84..45e3690e1d11 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -679,6 +679,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ 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 */ /** @@ -2929,8 +2938,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 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); } @@ -2938,7 +2948,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void resetAppOpsState() { if (mAppOp != OP_NONE && mAppOpVisibility) { - mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage()); + mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage(), + null /* featureId */); } } @@ -2953,11 +2964,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 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); } @@ -5684,11 +5696,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener, int waitingId) { - // TODO(b/148871522): Support child window 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); @@ -5702,11 +5716,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } 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/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 6f9d012d3145..29b2cd650565 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 @@ static bool readChunk(int fd, std::vector<uint8_t>& data) { 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 @@ private: } } 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 @@ private: // 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 @@ private: // 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 @@ private: 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 @@ private: } } - 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 @@ private: 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 @@ private: 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 @@ private: writeInstructions(instructions); } writeInstructions(instructions); + + { + std::lock_guard lock{mOutFdLock}; + mOutFd.reset(); + } } void writeInstructions(std::vector<IncFsDataBlock>& instructions) { @@ -743,7 +746,7 @@ private: 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 239a1011422c..fe05d4ecc2fc 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -178,63 +178,18 @@ bool processPowerHalReturn(bool isOk, const char* functionName) { return isOk; } -static void sendPowerHint(PowerHint hintId, uint32_t data) { - std::unique_lock<std::mutex> lock(gPowerHalMutex); - switch (connectPowerHalLocked()) { - case HalVersion::NONE: - return; - case HalVersion::HIDL_1_0: { - sp<IPowerV1_0> handle = gPowerHalHidlV1_0_; - lock.unlock(); - auto ret = handle->powerHint(hintId, data); - processPowerHalReturn(ret.isOk(), "powerHint"); - break; - } - case HalVersion::HIDL_1_1: { - sp<IPowerV1_1> handle = gPowerHalHidlV1_1_; - lock.unlock(); - auto ret = handle->powerHintAsync(hintId, data); - processPowerHalReturn(ret.isOk(), "powerHintAsync"); - break; - } - case HalVersion::AIDL: { - if (hintId == PowerHint::INTERACTION) { - sp<IPowerAidl> handle = gPowerHalAidl_; - lock.unlock(); - auto ret = handle->setBoost(Boost::INTERACTION, data); - processPowerHalReturn(ret.isOk(), "setBoost"); - 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"); - break; - } else { - ALOGE("Unsupported power hint: %s.", toString(hintId).c_str()); - return; - } - } - default: { - ALOGE("Unknown power HAL state"); - return; - } - } - SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId)); -} - enum class HalSupport { UNKNOWN = 0, ON, OFF, }; -static void setPowerBoost(Boost boost, int32_t durationMs) { +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}; + boostSupportedArray = {HalSupport::UNKNOWN}; // Quick return if boost is not supported by HAL if (boost > Boost::DISPLAY_UPDATE_IMMINENT || @@ -243,14 +198,6 @@ static void setPowerBoost(Boost boost, int32_t durationMs) { 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); @@ -267,12 +214,22 @@ static void setPowerBoost(Boost boost, int32_t durationMs) { processPowerHalReturn(ret.isOk(), "setPowerBoost"); } -static void setPowerMode(Mode mode, bool enabled) { +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}; + 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 || @@ -281,14 +238,6 @@ static void setPowerMode(Mode mode, bool enabled) { 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); @@ -304,6 +253,76 @@ static void setPowerMode(Mode mode, bool 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()) { + case HalVersion::NONE: + return; + case HalVersion::HIDL_1_0: { + sp<IPowerV1_0> handle = gPowerHalHidlV1_0_; + lock.unlock(); + auto ret = handle->powerHint(hintId, data); + processPowerHalReturn(ret.isOk(), "powerHint"); + break; + } + case HalVersion::HIDL_1_1: { + sp<IPowerV1_1> handle = gPowerHalHidlV1_1_; + lock.unlock(); + auto ret = handle->powerHintAsync(hintId, data); + processPowerHalReturn(ret.isOk(), "powerHintAsync"); + break; + } + case HalVersion::AIDL: { + if (hintId == PowerHint::INTERACTION) { + sp<IPowerAidl> handle = gPowerHalAidl_; + lock.unlock(); + setPowerBoostWithHandle(handle, Boost::INTERACTION, data); + break; + } else if (hintId == PowerHint::LAUNCH) { + sp<IPowerAidl> handle = gPowerHalAidl_; + lock.unlock(); + 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()); + return; + } + } + default: { + ALOGE("Unknown power HAL state"); + return; + } + } + SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId)); +} + 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 @@ static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean 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 @@ static void nativeSetFeature(JNIEnv* /* env */, jclass /* clazz */, jint feature 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 be11b8620bda..0277f16d5e54 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 @@ int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArra 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; - } - ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC)); - if (rfd.get() < 0) { - return errno; + + struct statx out = {}; + if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) { + return -errno; } - if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 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; } - 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 000000000000..11fc9031da3b --- /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 4895f343ad8a..7eacdf6cb253 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 @@ static Key KEYS[] = { } // 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 c832c18517ce..0e96bd7ae47e 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 void unassignSlot(int32_t pointerId) { } } +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 @@ NativeConnection* NativeConnection::open(const char* name, const char* uniqueId, 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)); + initKeysMap(); + + UInputDescriptor descriptor; + if (!descriptor.Open(name, uniqueId)) { 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; + // set the keys mapped + for (size_t i = 0; i < NELEM(KEYS); i++) { + descriptor.EnableKey(KEYS[i].linuxKeyCode); + } - // initialize keymap - initKeysMap(); + if (!descriptor.Create()) { + return nullptr; + } - // write device unique id to the phys property - ioctl(fd, UI_SET_PHYS, uniqueId); + return new NativeConnection(descriptor.Detach(), maxPointers, ConnectionType::kRemoteDevice); +} - // 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); +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; } - // 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; + // set the keys mapped for gamepads + for (size_t i = 0; i < NELEM(GAMEPAD_KEY_CODES); i++) { + descriptor.EnableKey(GAMEPAD_KEY_CODES[i]); } - if (ioctl(fd, UI_DEV_CREATE) != 0) { - ALOGE("Unable to create uinput device: %s.", strerror(errno)); - close(fd); + + // 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; } - ALOGV("Created uinput device, fd=%d.", fd); - return new NativeConnection(fd, maxPointers); + return new NativeConnection(descriptor.Detach(), 0, ConnectionType::kGamepadDevice); } void NativeConnection::sendEvent(int32_t type, int32_t code, int32_t value) { @@ -174,7 +310,6 @@ void NativeConnection::sendEvent(int32_t type, int32_t code, int32_t value) { 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 @@ static jlong nativeOpen(JNIEnv* env, jclass clazz, 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 nativeClose(JNIEnv* env, jclass clazz, jlong ptr) { 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 nativeSendKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyCode, jb } } +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 @@ static void nativeSendPointerUp(JNIEnv* env, jclass clazz, jlong ptr, 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 @@ static void nativeClear(JNIEnv* env, jclass clazz, jlong ptr) { 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); + } + } + } else { + for (size_t i = 0; i < NELEM(GAMEPAD_KEY_CODES); i++) { + connection->sendEvent(EV_KEY, GAMEPAD_KEY_CODES[i], 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); + 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 void nativeClear(JNIEnv* env, jclass clazz, jlong ptr) { */ 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 312d2d2e2ac2..f5d2c6a5c329 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.app.admin.DevicePolicyManager.WIPE_SILENTLY; 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; @@ -785,7 +786,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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. @@ -1198,7 +1199,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // 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; @@ -1439,10 +1440,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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)) { @@ -1691,7 +1693,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 = @@ -1929,8 +1931,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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="); @@ -3539,8 +3541,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +3667,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +3769,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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 +3805,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +3831,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void updateProtectedPackagesLocked(List<String> packages) { + private void updateUserControlDisabledPackagesLocked(List<String> packages) { mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages); } @@ -5874,6 +5877,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + 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 +6881,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException { - enforceProfileOrDeviceOwner(admin); + enforceNetworkStackOrProfileOrDeviceOwner(admin); final int userId = mInjector.userHandleGetCallingUserId(); return mInjector.binderWithCleanCallingIdentity( @@ -7786,7 +7797,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * 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 +7818,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * 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 +7832,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * 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 +7853,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * 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 +8831,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +9585,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 +15561,39 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @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; } } @@ -15716,18 +15728,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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 +15840,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @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 +15849,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // DO shouldn't be able to use this method. enforceProfileOwnerOfOrganizationOwnedDevice(admin); enforceHandlesCheckPolicyComplianceIntent(userId, admin.info.getPackageName()); - if (admin.mProfileMaximumTimeOff == timeoutMs) { + if (admin.mProfileMaximumTimeOffMillis == timeoutMillis) { return; } - admin.mProfileMaximumTimeOff = timeoutMs; + admin.mProfileMaximumTimeOffMillis = timeoutMillis; saveSettingsLocked(userId); } @@ -15850,7 +15862,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF) .setAdmin(who) - .setTimePeriod(timeoutMs) + .setTimePeriod(timeoutMillis) .write(); } @@ -15874,7 +15886,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 3fcb57a83cf5..2dbbc5ac6806 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -178,15 +178,9 @@ static std::tuple<int, incfs::FileId, incfs::NewFileParams> toMakeFileParams( 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 cccd01339177..727593664895 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -1155,7 +1155,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ // 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(); diff --git a/services/net/Android.bp b/services/net/Android.bp index dbc2df8369f8..c54102fb1d3d 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -20,6 +20,44 @@ java_library_static { ], } +// 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 dcefb537d0a0..8df2e0d08e0e 100644 --- a/services/net/java/android/net/IpMemoryStore.java +++ b/services/net/java/android/net/IpMemoryStore.java @@ -18,6 +18,7 @@ package android.net; 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 @@ public class IpMemoryStore extends IpMemoryStoreClient { 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 @@ public class IpMemoryStore extends IpMemoryStoreClient { } @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 aad75ae16aa9..fcf3a56de448 100644 --- a/services/net/java/android/net/TcpKeepalivePacketData.java +++ b/services/net/java/android/net/TcpKeepalivePacketData.java @@ -74,6 +74,19 @@ public class TcpKeepalivePacketData extends KeepalivePacketData implements Parce 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 @@ public class TcpKeepalivePacketData extends KeepalivePacketData implements Parce /** 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 @@ public class TcpKeepalivePacketData extends KeepalivePacketData implements Parce 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 a3618b47171a..b329aeec4853 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.content.Context; 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 @@ public class IpClientUtil { * * <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 602e4e1ac8ce..25ab5d36169e 100644 --- a/services/robotests/Android.bp +++ b/services/robotests/Android.bp @@ -27,7 +27,7 @@ android_app { "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 5160eae060f6..32587accf160 100644 --- a/services/robotests/backup/Android.bp +++ b/services/robotests/backup/Android.bp @@ -29,7 +29,7 @@ android_app { "services.net", ], - libs: ["ike-stubs"], + libs: ["android.net.ipsec.ike.stubs.system"], } //################################################################## diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index c94bb879f460..5c8220049d09 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 @@ public class RescuePartyTest { 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 @@ public class RescuePartyTest { @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 @@ public class RescuePartyTest { } @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 c45ee7b4d802..efe8119a62a5 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 @@ public class ApplicationExitInfoTest { 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 fe42dc92e949..7a175ca1b7f5 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.ActivityServiceConnectionsHolder; 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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 @@ public class MockingOomAdjusterTests { 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); 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 959dc0525e8f..de6f55ba1053 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 @@ public class AppOpsServiceTest { 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 164ee3184f5a..3ad905476190 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 @@ public class BiometricServiceTest { eq(0), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test @@ -264,7 +265,8 @@ public class BiometricServiceTest { eq(BiometricAuthenticator.TYPE_FACE), eq(false) /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test @@ -391,7 +393,8 @@ public class BiometricServiceTest { 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 @@ public class BiometricServiceTest { // 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 @@ public class BiometricServiceTest { eq(0 /* biometricModality */), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test @@ -460,7 +465,8 @@ public class BiometricServiceTest { // 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 @@ public class BiometricServiceTest { anyInt(), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - anyString()); + anyString(), + anyLong() /* sessionId */); } @Test @@ -627,8 +634,8 @@ public class BiometricServiceTest { 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 @@ public class BiometricServiceTest { eq(0 /* biometricModality */), anyBoolean() /* requireConfirmation */, anyInt() /* userId */, - eq(TEST_PACKAGE_NAME)); + eq(TEST_PACKAGE_NAME), + anyLong() /* sessionId */); } @Test @@ -825,8 +833,8 @@ public class BiometricServiceTest { 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 @@ public class BiometricServiceTest { 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 @@ public class BiometricServiceTest { 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 @@ public class BiometricServiceTest { 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 @@ public class BiometricServiceTest { 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 @@ public class BiometricServiceTest { 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 @@ public class BiometricServiceTest { 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 2944643b8d12..8be9213fd925 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 @@ public class CompatConfigTest { } @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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 2a6a24eab3d9..d038d6c1ca7f 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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) { 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 3dd150479ddc..d3a3e7e32ece 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 com.google.common.truth.Truth.assertThat; 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 @@ public class AppIntegrityManagerServiceImplTest { // 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 @@ public class AppIntegrityManagerServiceImplTest { 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/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 8da3bdfdf38c..155c6ddd4507 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.TvInputService; 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 @@ public class TunerResourceManagerServiceTest { } }; + 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 class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 64d481a2e6a0..6c8193074ef3 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_PEEK; 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; @@ -462,6 +460,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 +1157,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +1186,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .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 +1210,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +1229,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +1248,23 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +1276,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +2534,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 +2583,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 +2608,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 +2635,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 +3425,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +3447,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +3514,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 +4400,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { {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 +4411,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { {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 +4837,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +4861,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +4883,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +5551,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ -6168,6 +6234,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 +6265,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // 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 2a17bae57c8e..6b18cc64c5fb 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 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger { // 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 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger { 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 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger { } @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/ScheduleConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java index 551e1860d9b3..5a527a219055 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java @@ -3,8 +3,10 @@ package com.android.server.notification; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import android.app.Application; import android.content.Intent; import android.net.Uri; import android.service.notification.Condition; @@ -45,7 +47,7 @@ public class ScheduleConditionProviderTest extends UiServiceTestCase { null, // ActivityThread not actually used in Service ScheduleConditionProvider.class.getName(), null, // token not needed when not talking with the activity manager - null, + mock(Application.class), null // mocked services don't talk with the activity manager ); service.onCreate(); 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 c9c3649783b8..9e874211fcb3 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 @@ public class ActivityRecordTests extends ActivityTestsBase { 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/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index 12d89de8e9ad..8f3ff52b8018 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.graphics.Rect; 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 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { 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 class LaunchParamsPersisterTests extends ActivityTestsBase { 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 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { } @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 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { } @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 277bc41cf34e..67b1dacbb47b 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.assertFalse; 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 @@ public class RootWindowContainerTests extends WindowTestsBase { 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 ba4a82fc2d54..edf81ea32ad3 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 @@ public class SizeCompatTests extends ActivityTestsBase { 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()); + + // 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 Rect bounds = new Rect(mActivity.getBounds()); final int density = mActivity.getConfiguration().densityDpi; // change display configuration to fullscreen @@ -231,11 +237,7 @@ public class SizeCompatTests extends ActivityTestsBase { // 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 @@ public class SizeCompatTests extends ActivityTestsBase { 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 560d03fe7d9e..091f493b4687 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 @@ public class SystemServicesTestRule implements TestRule { } 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 251886ac309c..5007e3afd94f 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.doNothing; 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 @@ public class TaskOrganizerTests extends WindowTestsBase { } @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) @@ -557,6 +568,38 @@ public class TaskOrganizerTests extends WindowTestsBase { .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/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index ef9a73b794f7..9d48955c87be 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 @@ public class UsageStatsDatabase { // 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 @@ public class UsageStatsDatabase { } 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 0d1b3523bf9b..bbe9851520a1 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 2761127fb64e..17922560c93a 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -937,8 +937,8 @@ public class TelecomManager { */ 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; @@ -972,7 +972,7 @@ public class TelecomManager { 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); @@ -1170,7 +1170,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e); @@ -1196,7 +1196,7 @@ public class TelecomManager { 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(" + @@ -1500,7 +1500,7 @@ public class TelecomManager { 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); @@ -1522,7 +1522,7 @@ public class TelecomManager { 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); @@ -1543,7 +1543,7 @@ public class TelecomManager { 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); @@ -1565,7 +1565,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().isInCall(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling isInCall().", e); @@ -1591,7 +1591,7 @@ public class TelecomManager { try { if (isServiceConnected()) { return getTelecomService().isInManagedCall(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException calling isInManagedCall().", e); @@ -1772,7 +1772,7 @@ public class TelecomManager { 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); @@ -1797,7 +1797,7 @@ public class TelecomManager { 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); @@ -2027,7 +2027,7 @@ public class TelecomManager { if (service != null) { try { service.showInCallScreen(showDialpad, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#showCallScreen", e); } @@ -2090,7 +2090,7 @@ public class TelecomManager { } 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/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c20748bcb693..e2112a59beab 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2382,17 +2382,16 @@ public class CarrierConfigManager { * <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 @@ public class CarrierConfigManager { /** * 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 @@ public class CarrierConfigManager { /** * 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 @@ public class CarrierConfigManager { /** * 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 @@ public class CarrierConfigManager { * <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 @@ public class CarrierConfigManager { 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 34bac5de4c43..3984bd769edd 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -19,9 +19,7 @@ package android.telephony.ims; 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 @@ public class ImsManager { * 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/NetworkService.java b/telephony/java/android/telephony/NetworkService.java index 87d94bfda662..c75de42707a6 100644 --- a/telephony/java/android/telephony/NetworkService.java +++ b/telephony/java/android/telephony/NetworkService.java @@ -234,6 +234,9 @@ public abstract class NetworkService extends Service { * 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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 5a840de36cfb..01a40f55e29f 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1144,7 +1144,7 @@ public class SubscriptionManager { 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 @@ public class SubscriptionManager { 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 @@ public class SubscriptionManager { 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 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAllSubInfoList(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1322,7 +1322,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1373,7 +1373,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1491,7 +1491,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getAllSubInfoCount(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -1520,7 +1520,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { // ignore it @@ -2275,7 +2275,7 @@ public class SubscriptionManager { 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 @@ -2439,7 +2439,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { return iSub.isActiveSubId(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } } catch (RemoteException ex) { } @@ -2702,13 +2702,14 @@ public class SubscriptionManager { @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 @@ -2947,7 +2948,7 @@ public class SubscriptionManager { 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); } @@ -2956,7 +2957,8 @@ public class SubscriptionManager { 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 db5a0472ec30..3f106f7c3343 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -345,10 +345,10 @@ public class TelephonyManager { 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 @@ public class TelephonyManager { } } - 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; } @@ -1896,7 +1896,7 @@ public class TelephonyManager { try { return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -1938,7 +1938,7 @@ public class TelephonyManager { if (telephony == null) return null; return telephony.getDeviceIdWithFeature(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -1983,7 +1983,7 @@ public class TelephonyManager { 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 +2041,7 @@ public class TelephonyManager { 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 +2135,8 @@ public class TelephonyManager { 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 +2238,7 @@ public class TelephonyManager { 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 +2272,7 @@ public class TelephonyManager { } 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 +2356,7 @@ public class TelephonyManager { if (telephony == null) return null; return telephony.getNeighboringCellInfo(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -2961,7 +2962,7 @@ public class TelephonyManager { 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; @@ -3026,7 +3027,7 @@ public class TelephonyManager { 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; @@ -3063,7 +3064,7 @@ public class TelephonyManager { 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; @@ -3849,7 +3850,7 @@ public class TelephonyManager { if (info == null) return null; return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -3893,7 +3894,7 @@ public class TelephonyManager { 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; @@ -4122,7 +4123,7 @@ public class TelephonyManager { if (info == null) return null; return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4290,7 +4291,7 @@ public class TelephonyManager { if (info == null) return null; return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4314,7 +4315,7 @@ public class TelephonyManager { if (info == null) return null; return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4365,7 +4366,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } @@ -4377,7 +4378,7 @@ public class TelephonyManager { if (info == null) return null; return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4456,7 +4457,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) alphaTag = telephony.getLine1AlphaTagForDisplay(subId, - getOpPackageName(), getFeatureId()); + getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } @@ -4468,7 +4469,7 @@ public class TelephonyManager { if (info == null) return null; return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4498,7 +4499,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) return telephony.getMergedSubscriberIds(getSubId(), getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } @@ -4555,7 +4556,7 @@ public class TelephonyManager { 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) { @@ -4590,7 +4591,7 @@ public class TelephonyManager { if (info == null) return null; return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -4715,7 +4716,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getVisualVoicemailPackageName(mContext.getOpPackageName(), - getFeatureId(), getSubId()); + getAttributionTag(), getSubId()); } } catch (RemoteException ex) { } catch (NullPointerException ex) { @@ -5152,7 +5153,7 @@ public class TelephonyManager { if (telephony == null) return 0; return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return 0; } catch (NullPointerException ex) { @@ -5189,7 +5190,7 @@ public class TelephonyManager { if (info == null) return null; return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -5279,8 +5280,8 @@ public class TelephonyManager { * not present or not loaded * @hide */ + @UnsupportedAppUsage @Nullable - @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu() { try { @@ -5572,7 +5573,7 @@ public class TelephonyManager { (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."); @@ -5605,7 +5606,7 @@ public class TelephonyManager { if (telephony == null) return -1; return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { // the phone process is restarting. return -1; @@ -5629,7 +5630,7 @@ public class TelephonyManager { if (telephony == null) return -1; return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { // the phone process is restarting. return -1; @@ -5661,7 +5662,7 @@ public class TelephonyManager { if (telephony == null) return null; return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { // the phone process is restarting. return null; @@ -5753,7 +5754,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony == null) return null; - return telephony.getAllCellInfo(getOpPackageName(), getFeatureId()); + return telephony.getAllCellInfo(getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { } catch (NullPointerException ex) { } @@ -5853,7 +5854,7 @@ public class TelephonyManager { Binder.restoreCallingIdentity(identity); } } - }, getOpPackageName(), getFeatureId()); + }, getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { } } @@ -5904,7 +5905,7 @@ public class TelephonyManager { Binder.restoreCallingIdentity(identity); } } - }, getOpPackageName(), getFeatureId(), workSource); + }, getOpPackageName(), getAttributionTag(), workSource); } catch (RemoteException ex) { } } @@ -7190,7 +7191,7 @@ public class TelephonyManager { if (telephony == null) return null; return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName(), - getFeatureId()); + getAttributionTag()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { @@ -7224,7 +7225,7 @@ public class TelephonyManager { 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) { @@ -7245,7 +7246,7 @@ public class TelephonyManager { 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]; } @@ -7818,7 +7819,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } catch (RemoteException ex) { Rlog.e(TAG, "getAvailableNetworks RemoteException", ex); @@ -7873,7 +7874,7 @@ public class TelephonyManager { } } return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback, - getOpPackageName(), getFeatureId()); + getOpPackageName(), getAttributionTag()); } /** @@ -8627,7 +8628,7 @@ public class TelephonyManager { 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); } @@ -8921,7 +8922,10 @@ public class TelephonyManager { } /** - * 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 */ @@ -8934,7 +8938,8 @@ public class TelephonyManager { telephony.shutdownMobileRadios(); } } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#shutdownMobileRadios", e); + Log.e(TAG, "Error calling ITelephony#shutdownAllRadios", e); + e.rethrowAsRuntimeException(); } } @@ -8953,7 +8958,8 @@ public class TelephonyManager { 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; } @@ -8996,7 +9002,7 @@ public class TelephonyManager { 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. @@ -9183,7 +9189,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @CdmaRoamingMode int getCdmaRoamingMode() { int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT; @@ -9212,7 +9217,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) { try { @@ -9238,19 +9242,16 @@ public class TelephonyManager { /** 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 */ @@ -9269,7 +9270,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) { try { @@ -9387,7 +9387,7 @@ public class TelephonyManager { 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); } @@ -9404,7 +9404,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e); @@ -9423,7 +9423,7 @@ public class TelephonyManager { 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); @@ -10151,7 +10151,8 @@ public class TelephonyManager { 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); @@ -10292,7 +10293,7 @@ public class TelephonyManager { ITelephony service = getITelephony(); if (service != null) { return service.getServiceStateForSubscriber(subId, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e); @@ -11013,7 +11014,8 @@ public class TelephonyManager { 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); @@ -11124,21 +11126,21 @@ public class TelephonyManager { } /** - * 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()); @@ -11146,8 +11148,9 @@ public class TelephonyManager { if (telephony != null) retVal = telephony.isDataEnabled(subId); } catch (RemoteException e) { - Log.e(TAG, "Error isDataConnectionEnabled", e); + Log.e(TAG, "Error isDataConnectionAllowed", e); } catch (NullPointerException e) { + return false; } return retVal; } @@ -11298,7 +11301,7 @@ public class TelephonyManager { 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. @@ -11728,7 +11731,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.getEmergencyNumberList(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } else { throw new IllegalStateException("telephony service is null."); } @@ -11783,7 +11786,7 @@ public class TelephonyManager { 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); @@ -11876,7 +11879,7 @@ public class TelephonyManager { } /** - * 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} @@ -11885,6 +11888,7 @@ public class TelephonyManager { */ @TestApi @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion() { try { ITelephony telephony = getITelephony(); @@ -12091,13 +12095,13 @@ public class TelephonyManager { }) 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); @@ -12227,7 +12231,7 @@ public class TelephonyManager { 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); @@ -12332,7 +12336,7 @@ public class TelephonyManager { 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); @@ -12384,7 +12388,7 @@ public class TelephonyManager { ITelephony service = getITelephony(); if (service != null) { return service.doesSwitchMultiSimConfigTriggerReboot(getSubId(), - getOpPackageName(), getFeatureId()); + getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "doesSwitchMultiSimConfigTriggerReboot RemoteException", e); @@ -12453,7 +12457,6 @@ public class TelephonyManager { * @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) { @@ -12484,7 +12487,6 @@ public class TelephonyManager { * @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, @@ -12515,7 +12517,6 @@ public class TelephonyManager { * @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, @@ -12879,7 +12880,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean allow) { try { @@ -12908,7 +12908,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall() { try { @@ -12955,7 +12954,6 @@ public class TelephonyManager { * The IccLock state or password was changed successfully. * @hide */ - @SystemApi public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE; /** @@ -12968,7 +12966,6 @@ public class TelephonyManager { * * @hide */ - @SystemApi @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isIccLockEnabled() { @@ -13005,7 +13002,6 @@ public class TelephonyManager { * * @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."); @@ -13039,7 +13035,6 @@ public class TelephonyManager { * * @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 6c4e7ce51ebf..f56bbe13051c 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -458,6 +458,9 @@ public abstract class DataService extends Service { * 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 4b5303f42be0..bd531daff6b9 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -21,7 +21,6 @@ import android.Manifest; 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 @@ public class ImsMmTelManager implements RegistrationManager { * @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 @@ public class ImsMmTelManager implements RegistrationManager { @Override public void onTechnologyChangeFailed( @AccessNetworkConstants.TransportType int imsTransportType, - @Nullable ImsReasonInfo info) { + @NonNull ImsReasonInfo info) { } } diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 00fa94201297..1a606b7ae6a7 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -85,6 +85,7 @@ public class ProvisioningManager { /** * There is no existing configuration for the queried provisioning key. + * @hide */ public static final int PROVISIONING_RESULT_UNKNOWN = -1; @@ -120,6 +121,7 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * {@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 @@ public class ProvisioningManager { * {@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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * enabled. * @see #setProvisioningIntValue(int, int) * @see #getProvisioningIntValue(int) + * @hide */ public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; @@ -334,6 +355,7 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * {@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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * * @see #setProvisioningIntValue(int, int) * @see #getProvisioningIntValue(int) + * @hide */ public static final int KEY_LTE_THRESHOLD_2 = 57; @@ -707,6 +768,7 @@ public class ProvisioningManager { * * @see #setProvisioningIntValue(int, int) * @see #getProvisioningIntValue(int) + * @hide */ public static final int KEY_LTE_THRESHOLD_3 = 58; @@ -716,6 +778,7 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * * @see #setProvisioningIntValue(int, int) * @see #getProvisioningIntValue(int) + * @hide */ public static final int KEY_WIFI_THRESHOLD_A = 60; @@ -737,6 +801,7 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * * @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 57b9b7a30f8c..dc36edf5aad9 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -19,8 +19,6 @@ package android.telephony.ims; 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 @@ import java.util.Map; * 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 30306c7f9dea..05ab6bd75878 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -291,7 +291,7 @@ public class RcsUceAdapter { 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 @@ public class RcsUceAdapter { 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 5c86ba732701..1dbaff5df802 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -196,11 +196,11 @@ public interface RegistrationManager { } /** - * 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 interface RegistrationManager { */ 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 14a64d2585ed..7069e0ab9b1e 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -175,9 +175,11 @@ public class ImsRegistrationImplBase { */ 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 class ImsRegistrationImplBase { */ 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 f13371c1d0fa..8564f7affd6d 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -44,52 +44,62 @@ import java.lang.annotation.RetentionPolicy; 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 @@ public class ImsUtImplBase { /** * Constant used to denote an invalid return value. + * @hide */ public static final int INVALID_RESULT = -1; @@ -338,6 +349,7 @@ public class ImsUtImplBase { /** * 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/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java index 504bd1727682..b805744a8387 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 @@ import java.util.concurrent.TimeUnit; 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 Builder setRandomSeed(int randomSeed) { + mRandomSeed = randomSeed; + return this; + } + + public int getRandomSeed() { + return mRandomSeed; + } + + public Builder setFileSize(int fileSize) { + mFileSize = fileSize; + return this; + } - 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 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 c35385cd0429..654c1e21999d 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 @@ public class Utils { 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 59049160e885..c7e5a5ea3311 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 @@ public class MemoryUsageTest extends InstrumentationTestCase { 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 8cc8cf4d2a97..dfaac2cde91d 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -1063,6 +1063,20 @@ public class PackageWatchdogTest { assertThat(bootObserver2.mitigatedBootLoop()).isFalse(); } + /** + * 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(); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java index 61696718c76e..3bc530975580 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 @@ public class SurfaceControlViewHostTest extends Activity implements SurfaceHolde 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 e41517085c36..f254e4d3267a 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.util.AttributeSet; 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 @@ public class WindowInsetsActivity extends AppCompatActivity { && !mRequestedController) { mRequestedController = true; v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), - 1000, new LinearInterpolator(), + 1000, new LinearInterpolator(), null /* cancellationSignal */, mCurrentRequest = new WindowInsetsAnimationControlListener() { @Override public void onReady( @@ -208,7 +206,7 @@ public class WindowInsetsActivity extends AppCompatActivity { if ((types & ime()) != 0 && !hasControl) { hasControl = true; controller.controlWindowInsetsAnimation(ime(), -1, - new LinearInterpolator(), + new LinearInterpolator(), null /* cancellationSignal */, new WindowInsetsAnimationControlListener() { @Override public void onReady( diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java index 99dac1439b34..c74c112490f8 100644 --- a/tests/net/common/java/android/net/LinkAddressTest.java +++ b/tests/net/common/java/android/net/LinkAddressTest.java @@ -367,6 +367,9 @@ public class LinkAddressTest { -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 @@ public class LinkAddressTest { 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/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java index b81ca36429ff..442ac5605e90 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.IOnStatusListener; 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 @@ public class IpMemoryStoreTest { @Mock Context mMockContext; @Mock - NetworkStackClient mNetworkStackClient; + ModuleNetworkStackClient mModuleNetworkStackClient; @Mock IIpMemoryStore mMockService; @Mock @@ -90,14 +91,14 @@ public class IpMemoryStoreTest { ((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/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 8c0c36b89bf9..671c56499ad1 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.GET_PERMISSIONS; 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.assertTrue; 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 @@ public class ConnectivityServiceTest { assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); assertTrue(testFactory.getMyStartRequested()); - testFactory.unregister(); + testFactory.terminate(); if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback); handlerThread.quit(); } @@ -2452,6 +2451,38 @@ public class ConnectivityServiceTest { } @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.register(); + testFactory.expectAddRequestsWithScores(0); + 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 @@ public class ConnectivityServiceTest { cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); assertLength(1, mCm.getAllNetworks()); - testFactory.unregister(); + testFactory.terminate(); mCm.unregisterNetworkCallback(cellNetworkCallback); handlerThread.quit(); } @@ -3724,7 +3755,7 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { // 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 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(networkCallback); } - testFactory.unregister(); + testFactory.terminate(); handlerThread.quit(); } @@ -6870,8 +6901,13 @@ public class ConnectivityServiceTest { HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); // Verify onConnectivityReport fired - verify(mConnectivityDiagnosticsCallback) - .onConnectivityReport(any(ConnectivityReport.class)); + verify(mConnectivityDiagnosticsCallback).onConnectivityReport( + argThat(report -> { + final NetworkCapabilities nc = report.getNetworkCapabilities(); + return nc.getUids() == null + && nc.getAdministratorUids().isEmpty() + && nc.getOwnerUid() == Process.INVALID_UID; + })); } @Test @@ -6886,7 +6922,13 @@ public class ConnectivityServiceTest { 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/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 5aa32f868104..bcfce663db00 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -366,8 +366,12 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, }); 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/jarjar-rules.txt b/wifi/jarjar-rules.txt index 950361c1b244..eeb006ee6ab2 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 @@ rule com.android.internal.messages.SystemMessageProto* com.android.server.x.wifi # 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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 5e60b268504f..e1acaf83c227 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1353,7 +1353,7 @@ public class WifiManager { try { ParceledListSlice<WifiConfiguration> parceledList = mService.getConfiguredNetworks(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); if (parceledList == null) { return Collections.emptyList(); } @@ -1370,7 +1370,7 @@ public class WifiManager { try { ParceledListSlice<WifiConfiguration> parceledList = mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); if (parceledList == null) { return Collections.emptyList(); } @@ -1899,7 +1899,7 @@ public class WifiManager { @NonNull List<WifiNetworkSuggestion> networkSuggestions) { try { return mService.addNetworkSuggestions( - networkSuggestions, mContext.getOpPackageName(), mContext.getFeatureId()); + networkSuggestions, mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2681,8 +2681,8 @@ public class WifiManager { 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(); } @@ -2714,7 +2714,7 @@ public class WifiManager { public WifiInfo getConnectionInfo() { try { return mService.getConnectionInfo(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2729,7 +2729,7 @@ public class WifiManager { public List<ScanResult> getScanResults() { try { return mService.getScanResults(mContext.getOpPackageName(), - mContext.getFeatureId()); + mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2760,7 +2760,7 @@ public class WifiManager { try { return mService.getMatchingScanResults( networkSuggestionsToMatch, scanResults, - mContext.getOpPackageName(), mContext.getFeatureId()); + mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3199,7 +3199,7 @@ public class WifiManager { 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) { @@ -5891,7 +5891,7 @@ public class WifiManager { 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/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 6dbb0bd757d8..72ca900f4ecd 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -545,6 +545,7 @@ public final class WifiNetworkSuggestion implements Parcelable { mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED : WifiConfiguration.METERED_OVERRIDE_NONE; wifiConfiguration.carrierId = mCarrierId; + wifiConfiguration.trusted = !mIsNetworkUntrusted; return wifiConfiguration; } @@ -684,8 +685,7 @@ public final class WifiNetworkSuggestion implements Parcelable { mIsAppInteractionRequired, mIsUserInteractionRequired, mIsSharedWithUser, - mIsInitialAutojoinEnabled, - mIsNetworkUntrusted); + mIsInitialAutojoinEnabled); } } @@ -728,13 +728,6 @@ public final class WifiNetworkSuggestion implements Parcelable { */ 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 @@ public final class WifiNetworkSuggestion implements Parcelable { this.isUserInteractionRequired = false; this.isUserAllowedToManuallyConnect = true; this.isInitialAutoJoinEnabled = true; - this.isNetworkUntrusted = false; } /** @hide */ @@ -752,8 +744,7 @@ public final class WifiNetworkSuggestion implements Parcelable { boolean isAppInteractionRequired, boolean isUserInteractionRequired, boolean isUserAllowedToManuallyConnect, - boolean isInitialAutoJoinEnabled, - boolean isNetworkUntrusted) { + boolean isInitialAutoJoinEnabled) { checkNotNull(networkConfiguration); this.wifiConfiguration = networkConfiguration; this.passpointConfiguration = passpointConfiguration; @@ -762,7 +753,6 @@ public final class WifiNetworkSuggestion implements Parcelable { this.isUserInteractionRequired = isUserInteractionRequired; this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect; this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled; - this.isNetworkUntrusted = isNetworkUntrusted; } public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR = @@ -775,8 +765,7 @@ public final class WifiNetworkSuggestion implements Parcelable { in.readBoolean(), // isAppInteractionRequired in.readBoolean(), // isUserInteractionRequired in.readBoolean(), // isSharedCredentialWithUser - in.readBoolean(), // isAutojoinEnabled - in.readBoolean() + in.readBoolean() // isAutojoinEnabled ); } @@ -799,7 +788,6 @@ public final class WifiNetworkSuggestion implements Parcelable { dest.writeBoolean(isUserInteractionRequired); dest.writeBoolean(isUserAllowedToManuallyConnect); dest.writeBoolean(isInitialAutoJoinEnabled); - dest.writeBoolean(isNetworkUntrusted); } @Override @@ -842,7 +830,7 @@ public final class WifiNetworkSuggestion implements Parcelable { .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 @@ public final class WifiNetworkSuggestion implements Parcelable { /** @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 5e4891950466..d299cdc6cad8 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -182,7 +182,7 @@ public class WifiScanner { 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 @@ public class WifiScanner { 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 @@ public class WifiScanner { 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 @@ public class WifiScanner { 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 @@ public class WifiScanner { 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 @@ public class WifiScanner { 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 @@ public class WifiScanner { 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 2ebaa1805b2b..c2ae17c4bdeb 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -267,7 +267,7 @@ public class WifiAwareManager { 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 @@ public class WifiAwareManager { } 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 @@ public class WifiAwareManager { } 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 65e8b3d9283d..fa806e7797cd 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 @@ public final class Credential implements Parcelable { * @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 a310ff6404e7..724ccf0d7c45 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -1180,7 +1180,7 @@ public class WifiP2pManager { == 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 cb0c5d41df90..865702af695c 100644 --- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java +++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java @@ -146,8 +146,8 @@ public class WifiRttManager { 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/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index 51bf7380a1bb..01b2a8d4a24c 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -532,7 +532,7 @@ public class WifiNetworkSuggestionTest { 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 @@ public class WifiNetworkSuggestionTest { 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 @@ public class WifiNetworkSuggestionTest { 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 @@ public class WifiNetworkSuggestionTest { 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 @@ public class WifiNetworkSuggestionTest { 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 @@ public class WifiNetworkSuggestionTest { .setWpa2Passphrase(TEST_PRESHARED_KEY) .setUntrusted(true) .build(); - assertTrue(suggestion.isNetworkUntrusted); + assertTrue(suggestion.isUntrusted()); assertFalse(suggestion.isUserAllowedToManuallyConnect); } @@ -786,7 +786,7 @@ public class WifiNetworkSuggestionTest { .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 0cc76b68a15e..4881200aa2b7 100644 --- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java @@ -267,7 +267,7 @@ public class WifiScannerTest { 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 @@ public class WifiScannerTest { 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 c6825822f4cc..829d8f0a9a3a 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 @@ package android.net.wifi.hotspot2.pps; 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 class CredentialTest { 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 a9dcde07fd72..e6eae416ba78 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 @@ public class WifiRttManagerTest { mMockLooperExecutor = mMockLooper.getNewExecutor(); when(mockContext.getOpPackageName()).thenReturn(packageName); - when(mockContext.getFeatureId()).thenReturn(featureId); + when(mockContext.getAttributionTag()).thenReturn(featureId); } /** |