diff options
395 files changed, 9616 insertions, 3746 deletions
diff --git a/Android.bp b/Android.bp index 53417e8e8042..8adf48dc49b6 100644 --- a/Android.bp +++ b/Android.bp @@ -693,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", @@ -752,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", @@ -973,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",  } @@ -1240,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/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 86fb857817e5..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,7 +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 = 16844310; // 0x1010616 +    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 @@ -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 @@ -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); @@ -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); @@ -11894,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(); @@ -12023,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(); @@ -12047,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>); @@ -17353,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; @@ -20095,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); @@ -20119,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);    } @@ -27047,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(); @@ -37156,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 { @@ -43016,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); @@ -48996,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);    }  } @@ -53605,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 { @@ -55583,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); @@ -56741,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/system-current.txt b/api/system-current.txt index 0fb6919bac0a..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"; @@ -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); @@ -8943,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>); @@ -9368,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"; @@ -9427,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"; @@ -9798,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 { @@ -11540,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); @@ -11566,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 @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(); @@ -11605,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 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(); @@ -11623,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(); @@ -11645,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); @@ -11701,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"; @@ -12326,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; @@ -12595,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 { @@ -12679,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;    } @@ -12975,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    }  } @@ -13197,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(); @@ -13207,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 9045646ad656..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 @@ -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(); @@ -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 bd5bdc6c4f2f..713e92354fb4 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -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) @@ -7617,8 +7613,8 @@ message AttributedAppOps {      // 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) @@ -8490,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/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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index f6a79cd767da..fa4aa19d846e 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -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) { @@ -5890,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.           * 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/ContextImpl.java b/core/java/android/app/ContextImpl.java index 56e6aee83a44..9ccfe8df14c2 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -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 " 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/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/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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 347648a78886..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. @@ -11848,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();              } @@ -11867,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();              } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index f07123979529..514677e4867d 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -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/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index 052c9209f6f7..b4340729a220 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -435,10 +435,11 @@ public final class PermissionChecker {          final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);          final int opMode = (forDataDelivery)                  ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message) -                : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName); +                : 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: { @@ -467,12 +468,14 @@ public final class PermissionChecker {          final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);          final int opMode = (forDataDelivery)                  ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message) -                : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName); +                : 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/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/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/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 1e02a7d0d3cf..1304ba8ef3a0 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -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); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index d7c0dfbc65e2..3390f1616134 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -405,6 +405,8 @@ 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 @@ -1089,6 +1091,8 @@ 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); @@ -1247,6 +1251,8 @@ 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); @@ -2023,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;      } @@ -2493,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 0b673b5f89cd..9c13c85a041e 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -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();      /** diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 3ed0fd57975b..e41ed85d2438 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1820,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/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 972b0f55acef..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( @@ -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/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/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/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/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/Settings.java b/core/java/android/provider/Settings.java index ce9b4496f275..d8679b20e6dc 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8497,7 +8497,6 @@ public final class Settings {           *           * @hide           */ -        @SystemApi          public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";          /** 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/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 0cd96b8ea688..c52b02bb6a3c 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -55,7 +55,6 @@ import android.os.ServiceManager;  import android.os.UserHandle;  import android.util.ArrayMap;  import android.util.Log; -import android.util.Slog;  import android.widget.RemoteViews;  import com.android.internal.annotations.GuardedBy; @@ -1570,6 +1569,7 @@ public abstract class NotificationListenerService extends Service {          private boolean mVisuallyInterruptive;          private boolean mIsConversation;          private ShortcutInfo mShortcutInfo; +        private boolean mIsBubble;          private static final int PARCEL_VERSION = 2; @@ -1604,6 +1604,7 @@ public abstract class NotificationListenerService extends Service {              out.writeBoolean(mVisuallyInterruptive);              out.writeBoolean(mIsConversation);              out.writeParcelable(mShortcutInfo, flags); +            out.writeBoolean(mIsBubble);          }          /** @hide */ @@ -1639,6 +1640,7 @@ public abstract class NotificationListenerService extends Service {              mVisuallyInterruptive = in.readBoolean();              mIsConversation = in.readBoolean();              mShortcutInfo = in.readParcelable(cl); +            mIsBubble = in.readBoolean();          } @@ -1844,6 +1846,14 @@ public abstract class NotificationListenerService extends Service {          }          /** +         * Returns whether this notification is actively a bubble. +         * @hide +         */ +        public boolean isBubble() { +            return mIsBubble; +        } + +        /**           * @hide           */          public @Nullable ShortcutInfo getShortcutInfo() { @@ -1862,7 +1872,8 @@ public abstract class NotificationListenerService extends Service {                  int userSentiment, boolean hidden, long lastAudiblyAlertedMs,                  boolean noisy, ArrayList<Notification.Action> smartActions,                  ArrayList<CharSequence> smartReplies, boolean canBubble, -                boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo) { +                boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo, +                boolean isBubble) {              mKey = key;              mRank = rank;              mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -1886,6 +1897,7 @@ public abstract class NotificationListenerService extends Service {              mVisuallyInterruptive = visuallyInterruptive;              mIsConversation = isConversation;              mShortcutInfo = shortcutInfo; +            mIsBubble = isBubble;          }          /** @@ -1913,7 +1925,8 @@ public abstract class NotificationListenerService extends Service {                      other.mCanBubble,                      other.mVisuallyInterruptive,                      other.mIsConversation, -                    other.mShortcutInfo); +                    other.mShortcutInfo, +                    other.mIsBubble);          }          /** @@ -1970,7 +1983,8 @@ public abstract class NotificationListenerService extends Service {                      && Objects.equals(mIsConversation, other.mIsConversation)                      // Shortcutinfo doesn't have equals either; use id                      &&  Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()), -                    (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId())); +                    (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId())) +                    && Objects.equals(mIsBubble, other.mIsBubble);          }      } diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java index 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/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 a15d6c79417c..4227f78564a7 100644 --- a/core/java/android/view/InsetsAnimationControlCallbacks.java +++ b/core/java/android/view/InsetsAnimationControlCallbacks.java @@ -56,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 2b30c2dd658e..baee4123ef47 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -180,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) { @@ -191,6 +200,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll          }          setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */);          mFinished = true; +          mShownOnFinish = shown;      } @@ -207,6 +217,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll          }          mCancelled = true;          mListener.onCancelled(); + +        releaseLeashes();      }      public boolean isCancelled() { diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index 9c27802293b8..13b4cd83b4df 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -75,6 +75,12 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro              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 diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index c1763d62d829..123b9db75804 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -53,7 +53,6 @@ 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; @@ -518,14 +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.useInsetsAnimationThread); -            pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);              return;          } @@ -571,24 +569,26 @@ 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 */);      } @@ -599,18 +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,              boolean useInsetsAnimationThread) { -        CancellationSignal cancellationSignal = new CancellationSignal();          if (types == 0) {              // nothing to animate.              listener.onCancelled(); -            cancellationSignal.cancel(); -            return cancellationSignal; +            return;          }          cancelExistingControllers(types);          mLastStartedAnimTypes |= types; @@ -631,18 +630,19 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation                      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;          } @@ -654,13 +654,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation                          frame, mState, listener, typesReady, this, durationMs, interpolator, fade,                          animationType);          mRunningAnimations.add(new RunningAnimation(runner, animationType)); -        cancellationSignal.setOnCancelListener(runner::cancel); +        if (cancellationSignal != null) { +            cancellationSignal.setOnCancelListener(runner::cancel); +        }          if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {              showDirectly(types);          } else {              hideDirectly(types, false /* animationFinished */, animationType);          } -        return cancellationSignal;      }      /** @@ -704,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) { @@ -922,7 +923,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation          // 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, 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 879f2840a1b3..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 @@ -11750,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) { @@ -11760,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();          }      } @@ -20578,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;          } @@ -20654,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 @@ -24087,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) { @@ -28535,6 +28556,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,          int mSavedData;          String mStartActivityRequestWhoSaved;          boolean mIsAutofilled; +        boolean mHideHighlight;          int mAutofillViewId;          /** @@ -28558,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();          } @@ -28577,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);          } @@ -29075,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); @@ -29088,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/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/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/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 633aa2c5ffb4..ff03f1a1a2ab 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -205,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 @@ -310,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. @@ -317,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"); @@ -339,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 @@ -366,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"); @@ -393,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. @@ -718,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 0bfd65936b4a..ec1f516df5f3 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -761,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/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index 8446bbd09df7..e4a44084e91c 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -200,8 +200,9 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,          try {              return doInvoke();          } finally { -            if (isRecycleOnUse()) doRecycle(); -            if (!isRecycled()) { +            if (isRecycleOnUse()) { +                doRecycle(); +            } else if (!isRecycled()) {                  int argsSize = ArrayUtils.size(mArgs);                  for (int i = 0; i < argsSize; i++) {                      popArg(i); diff --git a/core/java/com/android/internal/view/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 dafb37761d0e..ea3c0fa9fc3c 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1567,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; @@ -1611,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); @@ -1650,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);    } @@ -2023,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)) { @@ -2060,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;  } @@ -2095,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); @@ -2225,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);  }  /** @@ -2426,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}, @@ -2439,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}, 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/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 4001defe51a9..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" /> 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 cb88aee33bfc..f3ca5ac9a4c4 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1831,6 +1831,19 @@          <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>      <!-- An attribution is a logical part of an app and is identified by a tag. 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 96807ccc2b22..7230cc49ac5b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3014,6 +3014,8 @@        <!-- @hide @SystemApi -->        <public name="minExtensionVersion" />        <public name="allowNativeHeapPointerTagging" /> +      <public name="requestDontAutoRevokePermissions" /> +      <public name="allowDontAutoRevokePermissions" />        <public name="preserveLegacyExternalStorage" />        <public name="mimeGroup" />        <public name="enableGwpAsan" /> 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/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index f1ab8ddebce6..42ab2e791641 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -58,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; @@ -74,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}. @@ -199,7 +198,7 @@ public class InsetsControllerTest {              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(); @@ -219,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(); @@ -498,7 +497,7 @@ public class InsetsControllerTest {              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); @@ -523,9 +522,10 @@ public class InsetsControllerTest {          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(); @@ -548,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(); @@ -572,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(); @@ -592,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(); @@ -613,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(); 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/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/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/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index 524b6c2e26e1..9dddcd4eaa3b 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -60,11 +60,6 @@ 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; 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/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java index b63162ba8090..3ee92bd7f3d0 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java @@ -238,10 +238,12 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks          }          buildNavBarContent(); -        // If the UI was rebuilt (day/night change) while the keyguard was up we need to -        // correctly respect that state. +        // If the UI was rebuilt (day/night change or user change) while the keyguard was up we need +        // to correctly respect that state.          if (mKeyguardStateControllerLazy.get().isShowing()) {              mCarNavigationBarController.showAllKeyguardButtons(isDeviceSetupForUser()); +        } else { +            mCarNavigationBarController.hideAllKeyguardButtons(isDeviceSetupForUser());          }          // Upon restarting the Navigation Bar, CarFacetButtonController should immediately apply the diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java index c04e47f557f7..f2748b89c95c 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java @@ -46,8 +46,6 @@ import org.mockito.ArgumentCaptor;  import org.mockito.Mock;  import org.mockito.MockitoAnnotations; -import dagger.Lazy; -  @RunWith(AndroidTestingRunner.class)  @TestableLooper.RunWithLooper  @SmallTest @@ -68,12 +66,8 @@ public class CarNavigationBarTest extends SysuiTestCase {      @Mock      private ButtonSelectionStateListener mButtonSelectionStateListener;      @Mock -    private Lazy<KeyguardStateController> mKeyguardStateControllerLazy; -    @Mock      private KeyguardStateController mKeyguardStateController;      @Mock -    private Lazy<NavigationBarController> mNavigationBarControllerLazy; -    @Mock      private NavigationBarController mNavigationBarController;      @Mock      private SuperStatusBarViewFactory mSuperStatusBarViewFactory; @@ -89,13 +83,11 @@ public class CarNavigationBarTest extends SysuiTestCase {          mCarNavigationBar = new CarNavigationBar(mContext, mCarNavigationBarController,                  mWindowManager, mDeviceProvisionedController, new CommandQueue(mContext),                  mAutoHideController, mButtonSelectionStateListener, mHandler, -                mKeyguardStateControllerLazy, mNavigationBarControllerLazy, +                () -> mKeyguardStateController, () -> mNavigationBarController,                  mSuperStatusBarViewFactory, mButtonSelectionStateController);          StatusBarWindowView statusBarWindowView = (StatusBarWindowView) LayoutInflater.from(                  mContext).inflate(R.layout.super_status_bar, /* root= */ null);          when(mSuperStatusBarViewFactory.getStatusBarWindowView()).thenReturn(statusBarWindowView); -        when(mNavigationBarControllerLazy.get()).thenReturn(mNavigationBarController); -        when(mKeyguardStateControllerLazy.get()).thenReturn(mKeyguardStateController);          when(mKeyguardStateController.isShowing()).thenReturn(false);          mDependency.injectMockDependency(WindowManager.class);          // Needed to inflate top navigation bar. @@ -119,4 +111,44 @@ public class CarNavigationBarTest extends SysuiTestCase {          verify(mButtonSelectionStateListener).onTaskStackChanged();      } + +    @Test +    public void restartNavBars_newUserNotSetupWithKeyguardShowing_showsKeyguardButtons() { +        ArgumentCaptor<CarDeviceProvisionedController.DeviceProvisionedListener> +                deviceProvisionedCallbackCaptor = ArgumentCaptor.forClass( +                CarDeviceProvisionedController.DeviceProvisionedListener.class); +        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); +        mCarNavigationBar.start(); +        when(mKeyguardStateController.isShowing()).thenReturn(true); +        // switching the currentUserSetup value to force restart the navbars. +        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false); +        verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture()); + +        deviceProvisionedCallbackCaptor.getValue().onUserSwitched(); +        waitForIdleSync(mHandler); + +        verify(mCarNavigationBarController).showAllKeyguardButtons(false); +    } + +    @Test +    public void restartNavbars_newUserIsSetupWithKeyguardHidden_hidesKeyguardButtons() { +        ArgumentCaptor<CarDeviceProvisionedController.DeviceProvisionedListener> +                deviceProvisionedCallbackCaptor = ArgumentCaptor.forClass( +                CarDeviceProvisionedController.DeviceProvisionedListener.class); +        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); +        mCarNavigationBar.start(); +        when(mKeyguardStateController.isShowing()).thenReturn(true); +        // switching the currentUserSetup value to force restart the navbars. +        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false); +        verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture()); +        deviceProvisionedCallbackCaptor.getValue().onUserSwitched(); +        waitForIdleSync(mHandler); +        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); +        when(mKeyguardStateController.isShowing()).thenReturn(false); + +        deviceProvisionedCallbackCaptor.getValue().onUserSetupChanged(); +        waitForIdleSync(mHandler); + +        verify(mCarNavigationBarController).hideAllKeyguardButtons(true); +    }  } diff --git a/packages/SettingsLib/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/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/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 10b47e69184c..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> 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/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/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 406e7cef9bfa..b39dd1aea4a9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -52,6 +52,7 @@ import android.content.res.Configuration;  import android.graphics.Rect;  import android.os.RemoteException;  import android.os.ServiceManager; +import android.service.notification.NotificationListenerService;  import android.service.notification.NotificationListenerService.RankingMap;  import android.service.notification.ZenModeConfig;  import android.util.ArraySet; @@ -146,13 +147,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi      private BubbleData mBubbleData;      @Nullable private BubbleStackView mStackView;      private BubbleIconFactory mBubbleIconFactory; -    private int mMaxBubbles;      // Tracks the id of the current (foreground) user.      private int mCurrentUserId;      // Saves notification keys of active bubbles when users are switched.      private final SparseSetArray<String> mSavedBubbleKeysPerUser; +    // Used when ranking updates occur and we check if things should bubble / unbubble +    private NotificationListenerService.Ranking mTmpRanking; +      // Saves notification keys of user created "fake" bubbles so that we can allow notifications      // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.      private final HashSet<String> mUserCreatedBubbles; @@ -338,7 +341,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi          configurationController.addCallback(this /* configurationListener */); -        mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);          mBubbleData = data;          mBubbleData.setListener(mBubbleDataListener);          mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() { @@ -939,9 +941,29 @@ public class BubbleController implements ConfigurationController.ConfigurationLi          }      } +    /** +     * Called when NotificationListener has received adjusted notification rank and reapplied +     * filtering and sorting. This is used to dismiss or create bubbles based on changes in +     * permissions on the notification channel or the global setting. +     * +     * @param rankingMap the updated ranking map from NotificationListenerService +     */      private void onRankingUpdated(RankingMap rankingMap) { -        // Forward to BubbleData to block any bubbles which should no longer be shown -        mBubbleData.notificationRankingUpdated(rankingMap); +        if (mTmpRanking == null) { +            mTmpRanking = new NotificationListenerService.Ranking(); +        } +        String[] orderedKeys = rankingMap.getOrderedKeys(); +        for (int i = 0; i < orderedKeys.length; i++) { +            String key = orderedKeys[i]; +            NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); +            rankingMap.getRanking(key, mTmpRanking); +            if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) { +                mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED); +            } else if (entry != null && mTmpRanking.isBubble()) { +                entry.setFlagBubble(true); +                onEntryUpdated(entry); +            } +        }      }      @SuppressWarnings("FieldCanBeLocal") diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index cf5a4d3840cc..ff5e13c25859 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -26,7 +26,6 @@ import android.app.Notification;  import android.app.PendingIntent;  import android.content.Context;  import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationListenerService.RankingMap;  import android.util.Log;  import android.util.Pair; @@ -207,12 +206,13 @@ 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 +225,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); @@ -289,31 +297,6 @@ public class BubbleData {      }      /** -     * Called when NotificationListener has received adjusted notification rank and reapplied -     * filtering and sorting. This is used to dismiss any bubbles which should no longer be shown -     * due to changes in permissions on the notification channel or the global setting. -     * -     * @param rankingMap the updated ranking map from NotificationListenerService -     */ -    public void notificationRankingUpdated(RankingMap rankingMap) { -        if (mTmpRanking == null) { -            mTmpRanking = new NotificationListenerService.Ranking(); -        } - -        String[] orderedKeys = rankingMap.getOrderedKeys(); -        for (int i = 0; i < orderedKeys.length; i++) { -            String key = orderedKeys[i]; -            if (hasBubbleWithKey(key)) { -                rankingMap.getRanking(key, mTmpRanking); -                if (!mTmpRanking.canBubble()) { -                    doRemove(key, BubbleController.DISMISS_BLOCKED); -                } -            } -        } -        dispatchPendingChanges(); -    } - -    /**       * Adds a group key indicating that the summary for this group should be suppressed.       *       * @param groupKey the group key of the group whose summary should be suppressed. diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 0778d5b54493..e666fb5f1af9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -471,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); 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 23dc4c65d2b1..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, @@ -144,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/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/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/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 2aac188c130d..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); 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/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index 047edd26c689..72d9d0ee8f8f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -206,7 +206,8 @@ public class NotificationListener extends NotificationListenerWithPlugins {                      false,                      false,                      false, -                    null +                    null, +                    false              );          }          return ranking; 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/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java index 98c45ffd6afb..596235cfb4d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java @@ -260,11 +260,14 @@ public class GroupCoalescer implements Dumpable {      private void applyRanking(RankingMap rankingMap) {          for (CoalescedEvent event : mCoalescedEvents.values()) {              Ranking ranking = new Ranking(); -            if (!rankingMap.getRanking(event.getKey(), ranking)) { -                throw new IllegalStateException( -                        "Ranking map doesn't contain key: " + event.getKey()); +            if (rankingMap.getRanking(event.getKey(), ranking)) { +                event.setRanking(ranking); +            } else { +                // TODO: (b/148791039) We should crash if we are ever handed a ranking with +                //  incomplete entries. Right now, there's a race condition in NotificationListener +                //  that means this might occur when SystemUI is starting up. +                mLogger.logMissingRanking(event.getKey());              } -            event.setRanking(ranking);          }      } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt index 6e8788db59d7..d4d5b64240c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt @@ -57,6 +57,14 @@ class GroupCoalescerLogger @Inject constructor(              "Modification of notif $str1 triggered TIMEOUT emit of batched group $str2"          })      } + +    fun logMissingRanking(forKey: String) { +        buffer.log(TAG, LogLevel.WARNING, { +            str1 = forKey +        }, { +            "RankingMap is missing an entry for coalesced notification $str1" +        }) +    }  }  private const val TAG = "GroupCoalescer"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/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/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/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/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/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/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java index fe8b89f381d6..a58000d09594 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java @@ -55,6 +55,7 @@ public class RankingBuilder {      private boolean mIsVisuallyInterruptive = false;      private boolean mIsConversation = false;      private ShortcutInfo mShortcutInfo = null; +    private boolean mIsBubble = false;      public RankingBuilder() {      } @@ -82,6 +83,7 @@ public class RankingBuilder {          mIsVisuallyInterruptive = ranking.visuallyInterruptive();          mIsConversation = ranking.isConversation();          mShortcutInfo = ranking.getShortcutInfo(); +        mIsBubble = ranking.isBubble();      }      public Ranking build() { @@ -108,7 +110,8 @@ public class RankingBuilder {                  mCanBubble,                  mIsVisuallyInterruptive,                  mIsConversation, -                mShortcutInfo); +                mShortcutInfo, +                mIsBubble);          return ranking;      } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 312bb7f08e72..972357e960ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -143,7 +143,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {                      IMPORTANCE_DEFAULT,                      null, null,                      null, null, null, true, sentiment, false, -1, false, null, null, false, false, -                    false, null); +                    false, null, false);              return true;          }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));      } @@ -162,7 +162,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {                      null, null,                      null, null, null, true,                      Ranking.USER_SENTIMENT_NEUTRAL, false, -1, -                    false, smartActions, null, false, false, false, null); +                    false, smartActions, null, false, false, false, null, false);              return true;          }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));      } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index a21a047d9a70..b568abbc8fde 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -284,7 +284,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {                  false,                  false,                  false, -                null); +                null, +                false);          mRankingMap = new NotificationListenerService.RankingMap(new Ranking[] {ranking});          TestableLooper.get(this).processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/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/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/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/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/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 0e9970ec7766..57bd42bfd05e 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -99,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; @@ -136,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. @@ -154,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. @@ -799,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. @@ -1680,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); @@ -2162,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(); @@ -2172,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; @@ -2197,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, @@ -2205,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!"); @@ -2663,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 a78caaced36c..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. 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 310664e10bf3..2e2241f5deea 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -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.AttributedOpEntry;  import android.app.AppOpsManager.OpFlags;  import android.app.AppOpsManagerInternal;  import android.app.AppOpsManagerInternal.CheckOpsDelegate; @@ -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; @@ -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: @@ -1657,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); @@ -3374,18 +3397,22 @@ public class AppOpsService extends IAppOpsService.Stub {          synchronized (this) {              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 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 { +            if (attributedOp.isRunning()) {                  attributedOp.finished(clientId); -            } catch (IllegalStateException e) { -                Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" -                        + packageName + " op=" + AppOpsManager.opToName(code), e); +            } else { +                Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" + packageName + "(" +                        + attributionTag + ") op=" + AppOpsManager.opToName(code));              }          }      } @@ -5614,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()); @@ -5644,7 +5671,7 @@ public class AppOpsService extends IAppOpsService.Stub {      private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid,              @NonNull String packageName, int opCode, @Nullable String attributionTag,              @NonNull String message) { -        switchPackageIfRarelyUsedLocked(packageName); +        switchPackageIfBootTimeOrRarelyUsedLocked(packageName);          if (!Objects.equals(mSampledPackage, packageName)) {              return;          } @@ -5695,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; @@ -5761,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/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/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java index c9c8042685d1..c96880cfebb8 100644 --- a/services/core/java/com/android/server/notification/BubbleExtractor.java +++ b/services/core/java/com/android/server/notification/BubbleExtractor.java @@ -15,9 +15,27 @@  */  package com.android.server.notification; +import static android.app.Notification.CATEGORY_CALL; +import static android.app.Notification.FLAG_BUBBLE; +import static android.app.Notification.FLAG_FOREGROUND_SERVICE; + +import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING; +import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Person;  import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo;  import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; + +import java.util.ArrayList; +  /**   * Determines whether a bubble can be shown for this notification   */ @@ -25,10 +43,15 @@ public class BubbleExtractor implements NotificationSignalExtractor {      private static final String TAG = "BubbleExtractor";      private static final boolean DBG = false; +    private BubbleChecker mBubbleChecker;      private RankingConfig mConfig; +    private ActivityManager mActivityManager; +    private Context mContext; -    public void initialize(Context ctx, NotificationUsageStats usageStats) { +    public void initialize(Context context, NotificationUsageStats usageStats) {          if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + "."); +        mContext = context; +        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);      }      public RankingReconsideration process(NotificationRecord record) { @@ -41,6 +64,12 @@ public class BubbleExtractor implements NotificationSignalExtractor {              if (DBG) Slog.d(TAG, "missing config");              return null;          } + +        if (mBubbleChecker == null) { +            if (DBG) Slog.d(TAG, "missing bubble checker"); +            return null; +        } +          boolean appCanShowBubble =                  mConfig.areBubblesAllowed(record.getSbn().getPackageName(), record.getSbn().getUid());          if (!mConfig.bubblesEnabled() || !appCanShowBubble) { @@ -52,7 +81,12 @@ public class BubbleExtractor implements NotificationSignalExtractor {                  record.setAllowBubble(appCanShowBubble);              }          } - +        final boolean applyFlag = mBubbleChecker.isNotificationAppropriateToBubble(record); +        if (applyFlag) { +            record.getNotification().flags |= FLAG_BUBBLE; +        } else { +            record.getNotification().flags &= ~FLAG_BUBBLE; +        }          return null;      } @@ -64,4 +98,185 @@ public class BubbleExtractor implements NotificationSignalExtractor {      @Override      public void setZenHelper(ZenModeHelper helper) {      } + +    /** +     * Expected to be called after {@link #setConfig(RankingConfig)} has occurred. +     */ +    void setShortcutHelper(ShortcutHelper helper) { +        if (mConfig == null) { +            if (DBG) Slog.d(TAG, "setting shortcut helper prior to setConfig"); +            return; +        } +        mBubbleChecker = new BubbleChecker(mContext, helper, mConfig, mActivityManager); +    } + +    @VisibleForTesting +    void setBubbleChecker(BubbleChecker checker) { +        mBubbleChecker = checker; +    } + +    /** +     * Encapsulates special checks to see if a notification can be flagged as a bubble. This +     * makes testing a bit easier. +     */ +    public static class BubbleChecker { + +        private ActivityManager mActivityManager; +        private RankingConfig mRankingConfig; +        private Context mContext; +        private ShortcutHelper mShortcutHelper; + +        BubbleChecker(Context context, ShortcutHelper helper, RankingConfig config, +                ActivityManager activityManager) { +            mContext = context; +            mActivityManager = activityManager; +            mShortcutHelper = helper; +            mRankingConfig = config; +        } + +        /** +         * @return whether the provided notification record is allowed to be represented as a +         * bubble, accounting for user choice & policy. +         */ +        public boolean isNotificationAppropriateToBubble(NotificationRecord r) { +            final String pkg = r.getSbn().getPackageName(); +            final int userId = r.getSbn().getUser().getIdentifier(); +            Notification notification = r.getNotification(); +            if (!canBubble(r, pkg, userId)) { +                // no log: canBubble has its own +                return false; +            } + +            if (mActivityManager.isLowRamDevice()) { +                logBubbleError(r.getKey(), "low ram device"); +                return false; +            } + +            // At this point the bubble must fulfill communication policy + +            // Communication always needs a person +            ArrayList<Person> peopleList = notification.extras != null +                    ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST) +                    : null; +            // Message style requires a person & it's not included in the list +            boolean isMessageStyle = Notification.MessagingStyle.class.equals( +                    notification.getNotificationStyle()); +            if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) { +                logBubbleError(r.getKey(), "Must have a person and be " +                        + "Notification.MessageStyle or Notification.CATEGORY_CALL"); +                return false; +            } + +            // Communication is a message or a call +            boolean isCall = CATEGORY_CALL.equals(notification.category); +            boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0; +            if (hasForegroundService && !isCall) { +                logBubbleError(r.getKey(), +                        "foreground services must be Notification.CATEGORY_CALL to bubble"); +                return false; +            } +            if (isMessageStyle) { +                return true; +            } else if (isCall) { +                if (hasForegroundService) { +                    return true; +                } +                logBubbleError(r.getKey(), "calls require foreground service"); +                return false; +            } +            logBubbleError(r.getKey(), "Must be " +                    + "Notification.MessageStyle or Notification.CATEGORY_CALL"); +            return false; +        } + +        /** +         * @return whether the user has enabled the provided notification to bubble, does not +         * account for policy. +         */ +        @VisibleForTesting +        boolean canBubble(NotificationRecord r, String pkg, int userId) { +            Notification notification = r.getNotification(); +            Notification.BubbleMetadata metadata = notification.getBubbleMetadata(); +            if (metadata == null) { +                // no log: no need to inform dev if they didn't attach bubble metadata +                return false; +            } +            if (!mRankingConfig.bubblesEnabled()) { +                logBubbleError(r.getKey(), "bubbles disabled for user: " + userId); +                return false; +            } +            if (!mRankingConfig.areBubblesAllowed(pkg, userId)) { +                logBubbleError(r.getKey(), +                        "bubbles for package: " + pkg + " disabled for user: " + userId); +                return false; +            } +            if (!r.getChannel().canBubble()) { +                logBubbleError(r.getKey(), +                        "bubbles for channel " + r.getChannel().getId() + " disabled"); +                return false; +            } + +            String shortcutId = metadata.getShortcutId(); +            boolean shortcutValid = shortcutId != null +                    && mShortcutHelper.hasValidShortcutInfo(shortcutId, pkg, r.getUser()); +            if (metadata.getBubbleIntent() == null && !shortcutValid) { +                // Should have a shortcut if intent is null +                logBubbleError(r.getKey(), +                        "couldn't find valid shortcut for bubble with shortcutId: " + shortcutId); +                return false; +            } +            if (shortcutValid) { +                return true; +            } +            // no log: canLaunch method has the failure log +            return canLaunchInActivityView(mContext, metadata.getBubbleIntent(), pkg); +        } + +        /** +         * Whether an intent is properly configured to display in an {@link +         * android.app.ActivityView}. +         * +         * @param context       the context to use. +         * @param pendingIntent the pending intent of the bubble. +         * @param packageName   the notification package name for this bubble. +         */ +        // Keep checks in sync with BubbleController#canLaunchInActivityView. +        @VisibleForTesting +        protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent, +                String packageName) { +            if (pendingIntent == null) { +                Slog.w(TAG, "Unable to create bubble -- no intent"); +                return false; +            } + +            Intent intent = pendingIntent.getIntent(); + +            ActivityInfo info = intent != null +                    ? intent.resolveActivityInfo(context.getPackageManager(), 0) +                    : null; +            if (info == null) { +                FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, +                        packageName, +                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING); +                Slog.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: " +                        + intent); +                return false; +            } +            if (!ActivityInfo.isResizeableMode(info.resizeMode)) { +                FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, +                        packageName, +                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE); +                Slog.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: " +                        + intent); +                return false; +            } +            return true; +        } + +        private void logBubbleError(String key, String failureMessage) { +            if (DBG) { +                Slog.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage); +            } +        } +    }  } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 69a5b35a5b12..20ad87a096a0 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -17,7 +17,6 @@  package com.android.server.notification;  import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; -import static android.app.Notification.CATEGORY_CALL;  import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;  import static android.app.Notification.FLAG_BUBBLE;  import static android.app.Notification.FLAG_FOREGROUND_SERVICE; @@ -51,9 +50,6 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;  import static android.content.Context.BIND_AUTO_CREATE;  import static android.content.Context.BIND_FOREGROUND_SERVICE;  import static android.content.Context.BIND_NOT_PERCEPTIBLE; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; -import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;  import static android.content.pm.PackageManager.FEATURE_LEANBACK;  import static android.content.pm.PackageManager.FEATURE_TELEVISION;  import static android.content.pm.PackageManager.MATCH_ALL; @@ -93,8 +89,6 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL  import static android.service.notification.NotificationListenerService.TRIM_LIGHT;  import static android.view.WindowManager.LayoutParams.TYPE_TOAST; -import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING; -import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;  import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;  import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;  import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES; @@ -131,8 +125,6 @@ import android.app.NotificationHistory.HistoricalNotification;  import android.app.NotificationManager;  import android.app.NotificationManager.Policy;  import android.app.PendingIntent; -import android.app.Person; -import android.app.RemoteInput;  import android.app.StatsManager;  import android.app.StatusBarManager;  import android.app.UriGrantsManager; @@ -152,7 +144,6 @@ import android.content.ContentResolver;  import android.content.Context;  import android.content.Intent;  import android.content.IntentFilter; -import android.content.pm.ActivityInfo;  import android.content.pm.ApplicationInfo;  import android.content.pm.IPackageManager;  import android.content.pm.LauncherApps; @@ -160,7 +151,6 @@ import android.content.pm.PackageManager;  import android.content.pm.PackageManager.NameNotFoundException;  import android.content.pm.PackageManagerInternal;  import android.content.pm.ParceledListSlice; -import android.content.pm.ShortcutInfo;  import android.content.pm.UserInfo;  import android.content.res.Resources;  import android.database.ContentObserver; @@ -251,7 +241,6 @@ import com.android.internal.util.ArrayUtils;  import com.android.internal.util.CollectionUtils;  import com.android.internal.util.DumpUtils;  import com.android.internal.util.FastXmlSerializer; -import com.android.internal.util.FrameworkStatsLog;  import com.android.internal.util.Preconditions;  import com.android.internal.util.XmlUtils;  import com.android.internal.util.function.TriPredicate; @@ -296,7 +285,6 @@ import java.nio.charset.StandardCharsets;  import java.util.ArrayDeque;  import java.util.ArrayList;  import java.util.Arrays; -import java.util.HashMap;  import java.util.Iterator;  import java.util.List;  import java.util.Map.Entry; @@ -421,7 +409,7 @@ public class NotificationManagerService extends SystemService {      private RoleObserver mRoleObserver;      private UserManager mUm;      private IPlatformCompat mPlatformCompat; -    private LauncherApps mLauncherAppsService; +    private ShortcutHelper mShortcutHelper;      final IBinder mForegroundToken = new Binder();      private WorkerHandler mHandler; @@ -497,7 +485,8 @@ public class NotificationManagerService extends SystemService {              "allow-secure-notifications-on-lockscreen";      private static final String LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE = "value"; -    private RankingHelper mRankingHelper; +    @VisibleForTesting +    RankingHelper mRankingHelper;      @VisibleForTesting      PreferencesHelper mPreferencesHelper; @@ -931,6 +920,8 @@ 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 +961,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 +996,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 +1008,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 +1099,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 +1124,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 +1169,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( @@ -1186,13 +1192,30 @@ public class NotificationManagerService extends SystemService {          @Override          public void onNotificationBubbleChanged(String key, boolean isBubble) { +            String pkg; +            synchronized (mNotificationLock) { +                NotificationRecord r = mNotificationsByKey.get(key); +                pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null; +            } +            boolean isAppForeground = pkg != null +                    && mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;              synchronized (mNotificationLock) {                  NotificationRecord r = mNotificationsByKey.get(key);                  if (r != null) { -                    final StatusBarNotification n = r.getSbn(); -                    final int callingUid = n.getUid(); -                    final String pkg = n.getPackageName(); -                    applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isBubble); +                    if (!isBubble) { +                        // This happens if the user has dismissed the bubble but the notification +                        // is still active in the shade, enqueuing would create a bubble since +                        // the notification is technically allowed. Flip the flag so that +                        // apps querying noMan will know that their notification is not showing +                        // as a bubble. +                        r.getNotification().flags &= ~FLAG_BUBBLE; +                    } else { +                        // Enqueue will trigger resort & if the flag is allowed to be true it'll +                        // be applied there. +                        r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE; +                        mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), +                                r, isAppForeground)); +                    }                  }              }          } @@ -1219,6 +1242,7 @@ public class NotificationManagerService extends SystemService {                          flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;                      }                      data.setFlags(flags); +                    r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;                      mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r,                              true /* isAppForeground */));                  } @@ -1310,6 +1334,9 @@ 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);          }      } @@ -1595,80 +1622,6 @@ public class NotificationManagerService extends SystemService {          }      }; -    // Key: packageName Value: <shortcutId, notifId> -    private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>(); - -    private boolean mLauncherAppsCallbackRegistered; - -    // Bubbles can be created based on a shortcut, we need to listen for changes to -    // that shortcut so that we may update the bubble appropriately. -    private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() { -        @Override -        public void onPackageRemoved(String packageName, UserHandle user) { -        } - -        @Override -        public void onPackageAdded(String packageName, UserHandle user) { -        } - -        @Override -        public void onPackageChanged(String packageName, UserHandle user) { -        } - -        @Override -        public void onPackagesAvailable(String[] packageNames, UserHandle user, -                boolean replacing) { -        } - -        @Override -        public void onPackagesUnavailable(String[] packageNames, UserHandle user, -                boolean replacing) { -        } - -        @Override -        public void onShortcutsChanged(@NonNull String packageName, -                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { -            HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName); -            boolean isAppForeground = packageName != null -                    && mActivityManager.getPackageImportance(packageName) == IMPORTANCE_FOREGROUND; -            ArrayList<String> bubbleKeysToRemove = new ArrayList<>(); -            if (shortcutBubbles != null) { -                // If we can't find one of our bubbles in the shortcut list, that bubble needs -                // to be removed. -                for (String shortcutId : shortcutBubbles.keySet()) { -                    boolean foundShortcut = false; -                    for (int i = 0; i < shortcuts.size(); i++) { -                        if (shortcuts.get(i).getId().equals(shortcutId)) { -                            foundShortcut = true; -                            break; -                        } -                    } -                    if (!foundShortcut) { -                        bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId)); -                    } -                } -            } - -            // Do the removals -            for (int i = 0; i < bubbleKeysToRemove.size(); i++) { -                // update flag bubble -                String bubbleKey = bubbleKeysToRemove.get(i); -                synchronized (mNotificationLock) { -                    NotificationRecord r = mNotificationsByKey.get(bubbleKey); -                    if (r != null) { -                        final StatusBarNotification n = r.getSbn(); -                        final int callingUid = n.getUid(); -                        final String pkg = n.getPackageName(); -                        applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isAppForeground); -                        mHandler.post(new EnqueueNotificationRunnable(user.getIdentifier(), r, -                                false /* isAppForeground */)); -                    } -                } -            } -        } -    }; - -      private final class SettingsObserver extends ContentObserver {          private final Uri NOTIFICATION_BADGING_URI                  = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING); @@ -1763,8 +1716,8 @@ public class NotificationManagerService extends SystemService {      }      @VisibleForTesting -    void setLauncherApps(LauncherApps launcherApps) { -        mLauncherAppsService = launcherApps; +    ShortcutHelper getShortcutHelper() { +        return mShortcutHelper;      }      @VisibleForTesting @@ -2314,8 +2267,13 @@ public class NotificationManagerService extends SystemService {              mRoleObserver = new RoleObserver(getContext().getSystemService(RoleManager.class),                      mPackageManager, getContext().getMainExecutor());              mRoleObserver.init(); -            mLauncherAppsService = +            LauncherApps launcherApps =                      (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE); +            mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener); +            BubbleExtractor bubbsExtractor = mRankingHelper.findExtractor(BubbleExtractor.class); +            if (bubbsExtractor != null) { +                bubbsExtractor.setShortcutHelper(mShortcutHelper); +            }              registerNotificationPreferencesPullers();          } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {              // This observer will force an update when observe is called, causing us to @@ -3458,7 +3416,7 @@ public class NotificationManagerService extends SystemService {              ArrayList<ConversationChannelWrapper> conversations =                      mPreferencesHelper.getConversations(onlyImportant);              for (ConversationChannelWrapper conversation : conversations) { -                conversation.setShortcutInfo(getShortcutInfo( +                conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(                          conversation.getNotificationChannel().getConversationId(),                          conversation.getPkg(),                          UserHandle.of(UserHandle.getUserId(conversation.getUid())))); @@ -3481,7 +3439,7 @@ public class NotificationManagerService extends SystemService {              ArrayList<ConversationChannelWrapper> conversations =                      mPreferencesHelper.getConversations(pkg, uid);              for (ConversationChannelWrapper conversation : conversations) { -                conversation.setShortcutInfo(getShortcutInfo( +                conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(                          conversation.getNotificationChannel().getConversationId(),                          pkg,                          UserHandle.of(UserHandle.getUserId(uid)))); @@ -5652,7 +5610,7 @@ public class NotificationManagerService extends SystemService {              }          } -        r.setShortcutInfo(getShortcutInfo(notification.getShortcutId(), pkg, user)); +        r.setShortcutInfo(mShortcutHelper.getShortcutInfo(notification.getShortcutId(), pkg, user));          if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,                  r.getSbn().getOverrideGroupKey() != null)) { @@ -5780,16 +5738,12 @@ public class NotificationManagerService extends SystemService {      }      /** -     * Updates the flags for this notification to reflect whether it is a bubble or not. Some -     * bubble specific flags only work if the app is foreground, this will strip those flags +     * Some bubble specific flags only work if the app is foreground, this will strip those flags       * if the app wasn't foreground.       */ -    private void updateNotificationBubbleFlags(NotificationRecord r, String pkg, int userId, -            NotificationRecord oldRecord, boolean isAppForeground) { -        Notification notification = r.getNotification(); -        applyFlagBubble(r, pkg, userId, oldRecord, true /* desiredFlag */); - +    private void updateNotificationBubbleFlags(NotificationRecord r, boolean isAppForeground) {          // Remove any bubble specific flags that only work when foregrounded +        Notification notification = r.getNotification();          Notification.BubbleMetadata metadata = notification.getBubbleMetadata();          if (!isAppForeground && metadata != null) {              int flags = metadata.getFlags(); @@ -5799,252 +5753,30 @@ public class NotificationManagerService extends SystemService {          }      } -    /** -     * Handles actually applying or removing {@link Notification#FLAG_BUBBLE}. Performs necessary -     * checks for the provided record to see if it can actually be a bubble. -     * Tracks shortcut based bubbles so that we can find out if they've changed or been removed. -     */ -    private void applyFlagBubble(NotificationRecord r, String pkg, int userId, -            NotificationRecord oldRecord, boolean desiredFlag) { -        boolean applyFlag = desiredFlag -                && isNotificationAppropriateToBubble(r, pkg, userId, oldRecord); -        final String shortcutId = r.getNotification().getBubbleMetadata() != null -                ? r.getNotification().getBubbleMetadata().getShortcutId() -                : null; -        if (applyFlag) { -            if (shortcutId != null) { -                // Must track shortcut based bubbles in case the shortcut is removed -                HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get( -                        r.getSbn().getPackageName()); -                if (packageBubbles == null) { -                    packageBubbles = new HashMap<>(); -                } -                packageBubbles.put(shortcutId, r.getKey()); -                mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles); -                if (!mLauncherAppsCallbackRegistered) { -                    mLauncherAppsService.registerCallback(mLauncherAppsCallback, mHandler); -                    mLauncherAppsCallbackRegistered = true; -                } -            } -            r.getNotification().flags |= FLAG_BUBBLE; -        } else { -            if (shortcutId != null) { -                // No longer track shortcut -                HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get( -                        r.getSbn().getPackageName()); -                if (packageBubbles != null) { -                    packageBubbles.remove(shortcutId); -                } -                if (packageBubbles != null && packageBubbles.isEmpty()) { -                    mActiveShortcutBubbles.remove(r.getSbn().getPackageName()); -                } -                if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) { -                    mLauncherAppsService.unregisterCallback(mLauncherAppsCallback); -                    mLauncherAppsCallbackRegistered = false; -                } -            } -            r.getNotification().flags &= ~FLAG_BUBBLE; -        } -    } - -    /** -     * @return whether the provided notification record is allowed to be represented as a bubble, -     * accounting for user choice & policy. -     */ -    private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId, -            NotificationRecord oldRecord) { -        Notification notification = r.getNotification(); -        if (!canBubble(r, pkg, userId)) { -            // no log: canBubble has its own -            return false; -        } - -        if (mActivityManager.isLowRamDevice()) { -            logBubbleError(r.getKey(), "low ram device"); -            return false; -        } - -        if (oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0) { -            // This is an update to an active bubble -            return true; -        } - -        // At this point the bubble must fulfill communication policy - -        // Communication always needs a person -        ArrayList<Person> peopleList = notification.extras != null -                ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST) -                : null; -        // Message style requires a person & it's not included in the list -        boolean isMessageStyle = Notification.MessagingStyle.class.equals( -                notification.getNotificationStyle()); -        if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) { -            logBubbleError(r.getKey(), "Must have a person and be " -                    + "Notification.MessageStyle or Notification.CATEGORY_CALL"); -            return false; -        } - -        // Communication is a message or a call -        boolean isCall = CATEGORY_CALL.equals(notification.category); -        boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0; -        if (hasForegroundService && !isCall) { -            logBubbleError(r.getKey(), -                    "foreground services must be Notification.CATEGORY_CALL to bubble"); -            return false; -        } -        if (isMessageStyle) { -            if (hasValidRemoteInput(notification)) { -                return true; -            } -            logBubbleError(r.getKey(), "messages require valid remote input"); -            return false; -        } else if (isCall) { -            if (hasForegroundService) { -                return true; -            } -            logBubbleError(r.getKey(), "calls require foreground service"); -            return false; -        } -        logBubbleError(r.getKey(), "Must be " -                + "Notification.MessageStyle or Notification.CATEGORY_CALL"); -        return false; -    } - -    /** -     * @return whether the user has enabled the provided notification to bubble, does not account -     * for policy. -     */ -    private boolean canBubble(NotificationRecord r, String pkg, int userId) { -        Notification notification = r.getNotification(); -        Notification.BubbleMetadata metadata = notification.getBubbleMetadata(); -        if (metadata == null) { -            // no log: no need to inform dev if they didn't attach bubble metadata -            return false; -        } -        if (!mPreferencesHelper.bubblesEnabled()) { -            logBubbleError(r.getKey(), "bubbles disabled for user: " + userId); -            return false; -        } -        if (!mPreferencesHelper.areBubblesAllowed(pkg, userId)) { -            logBubbleError(r.getKey(), -                    "bubbles for package: " + pkg + " disabled for user: " + userId); -            return false; -        } -        if (!r.getChannel().canBubble()) { -            logBubbleError(r.getKey(), -                    "bubbles for channel " + r.getChannel().getId() + " disabled"); -            return false; -        } - -        String shortcutId = metadata.getShortcutId(); -        boolean shortcutValid = shortcutId != null -                && hasValidShortcutInfo(shortcutId, pkg, r.getUser()); -        if (metadata.getBubbleIntent() == null && !shortcutValid) { -            // Should have a shortcut if intent is null -            logBubbleError(r.getKey(), "couldn't find shortcutId for bubble: " + shortcutId); -            return false; -        } -        if (shortcutValid) { -            return true; -        } -        // no log: canLaunch method has the failure log -        return canLaunchInActivityView(getContext(), metadata.getBubbleIntent(), pkg); -    } - -    private boolean hasValidRemoteInput(Notification n) { -        // Also check for inline reply -        Notification.Action[] actions = n.actions; -        if (actions != null) { -            // Get the remote inputs -            for (int i = 0; i < actions.length; i++) { -                Notification.Action action = actions[i]; -                RemoteInput[] inputs = action.getRemoteInputs(); -                if (inputs != null && inputs.length > 0) { -                    return true; +    private ShortcutHelper.ShortcutListener mShortcutListener = +            new ShortcutHelper.ShortcutListener() { +                @Override +                public void onShortcutRemoved(String key) { +                    String packageName; +                    synchronized (mNotificationLock) { +                        NotificationRecord r = mNotificationsByKey.get(key); +                        packageName = r != null ? r.getSbn().getPackageName() : null; +                    } +                    boolean isAppForeground = packageName != null +                            && mActivityManager.getPackageImportance(packageName) +                            == IMPORTANCE_FOREGROUND; +                    synchronized (mNotificationLock) { +                        NotificationRecord r = mNotificationsByKey.get(key); +                        if (r != null) { +                            // Enqueue will trigger resort & flag is updated that way. +                            r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE; +                            mHandler.post( +                                    new NotificationManagerService.EnqueueNotificationRunnable( +                                            r.getUser().getIdentifier(), r, isAppForeground)); +                        } +                    }                  } -            } -        } -        return false; -    } - -    private ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) { -        final long token = Binder.clearCallingIdentity(); -        try { -            if (shortcutId == null || packageName == null || user == null) { -                return null; -            } -            LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); -            if (packageName != null) { -                query.setPackage(packageName); -            } -            if (shortcutId != null) { -                query.setShortcutIds(Arrays.asList(shortcutId)); -            } -            query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED); -            List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user); -            ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0 -                    ? shortcuts.get(0) -                    : null; -            return shortcutInfo; -        } finally { -            Binder.restoreCallingIdentity(token); -        } -    } - -    private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) { -        ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user); -        return shortcutInfo != null && shortcutInfo.isLongLived(); -    } - -    private void logBubbleError(String key, String failureMessage) { -        if (DBG) { -            Log.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage); -        } -    } -    /** -     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. -     * -     * @param context       the context to use. -     * @param pendingIntent the pending intent of the bubble. -     * @param packageName   the notification package name for this bubble. -     */ -    // Keep checks in sync with BubbleController#canLaunchInActivityView. -    @VisibleForTesting -    protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent, -            String packageName) { -        if (pendingIntent == null) { -            Log.w(TAG, "Unable to create bubble -- no intent"); -            return false; -        } - -        // Need escalated privileges to get the intent. -        final long token = Binder.clearCallingIdentity(); -        Intent intent; -        try { -            intent = pendingIntent.getIntent(); -        } finally { -            Binder.restoreCallingIdentity(token); -        } - -        ActivityInfo info = intent != null -                ? intent.resolveActivityInfo(context.getPackageManager(), 0) -                : null; -        if (info == null) { -            FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, -                    BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING); -            Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: " -                    + intent); -            return false; -        } -        if (!ActivityInfo.isResizeableMode(info.resizeMode)) { -            FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, -                    BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE); -            Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: " -                    + intent); -            return false; -        } -        return true; -    } +            };      private void doChannelWarningToast(CharSequence toastText) {          Binder.withCleanCallingIdentity(() -> { @@ -6150,6 +5882,9 @@ 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 +6014,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); @@ -6406,6 +6143,8 @@ public class NotificationManagerService extends SystemService {                      cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName,                              mSendDelete, childrenFlagChecker);                      updateLightsLocked(); +                    mShortcutHelper.maybeListenForShortcutChangesForBubbles(r, true /* isRemoved */, +                            mHandler);                  } else {                      // No notification was found, assume that it is snoozed and cancel it.                      if (mReason != REASON_SNOOZED) { @@ -6473,7 +6212,7 @@ public class NotificationManagerService extends SystemService {                  final String tag = n.getTag();                  // We need to fix the notification up a little for bubbles -                updateNotificationBubbleFlags(r, pkg, callingUid, old, isAppForeground); +                updateNotificationBubbleFlags(r, isAppForeground);                  // Handle grouped notifications and bail out early if we                  // can to avoid extracting signals. @@ -6643,6 +6382,10 @@ public class NotificationManagerService extends SystemService {                                  + n.getPackageName());                      } +                    mShortcutHelper.maybeListenForShortcutChangesForBubbles(r, +                            false /* isRemoved */, +                            mHandler); +                      maybeRecordInterruptionLocked(r);                      // Log event to statsd @@ -7402,6 +7145,7 @@ public class NotificationManagerService extends SystemService {              int[] visibilities = new int[N];              boolean[] showBadges = new boolean[N];              boolean[] allowBubbles = new boolean[N]; +            boolean[] isBubble = new boolean[N];              ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);              ArrayList<String> groupKeyBefore = new ArrayList<>(N);              ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N); @@ -7417,6 +7161,7 @@ public class NotificationManagerService extends SystemService {                  visibilities[i] = r.getPackageVisibilityOverride();                  showBadges[i] = r.canShowBadge();                  allowBubbles[i] = r.canBubble(); +                isBubble[i] = r.getNotification().isBubbleNotification();                  channelBefore.add(r.getChannel());                  groupKeyBefore.add(r.getGroupKey());                  overridePeopleBefore.add(r.getPeopleOverride()); @@ -7435,6 +7180,7 @@ public class NotificationManagerService extends SystemService {                          || visibilities[i] != r.getPackageVisibilityOverride()                          || showBadges[i] != r.canShowBadge()                          || allowBubbles[i] != r.canBubble() +                        || isBubble[i] != r.getNotification().isBubbleNotification()                          || !Objects.equals(channelBefore.get(i), r.getChannel())                          || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())                          || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride()) @@ -8597,7 +8343,8 @@ public class NotificationManagerService extends SystemService {                      record.canBubble(),                      record.isInterruptive(),                      record.isConversation(), -                    record.getShortcutInfo() +                    record.getShortcutInfo(), +                    record.getNotification().isBubbleNotification()              );              rankings.add(ranking);          } diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index 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/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java new file mode 100644 index 000000000000..7bbb3b117517 --- /dev/null +++ b/services/core/java/com/android/server/notification/ShortcutHelper.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.notification; + +import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED; +import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; +import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; + +import android.annotation.NonNull; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutInfo; +import android.os.Binder; +import android.os.Handler; +import android.os.UserHandle; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * Helper for querying shortcuts. + */ +class ShortcutHelper { + +    /** +     * Listener to call when a shortcut we're tracking has been removed. +     */ +    interface ShortcutListener { +        void onShortcutRemoved(String key); +    } + +    private LauncherApps mLauncherAppsService; +    private ShortcutListener mShortcutListener; + +    // Key: packageName Value: <shortcutId, notifId> +    private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>(); +    private boolean mLauncherAppsCallbackRegistered; + +    // Bubbles can be created based on a shortcut, we need to listen for changes to +    // that shortcut so that we may update the bubble appropriately. +    private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() { +        @Override +        public void onPackageRemoved(String packageName, UserHandle user) { +        } + +        @Override +        public void onPackageAdded(String packageName, UserHandle user) { +        } + +        @Override +        public void onPackageChanged(String packageName, UserHandle user) { +        } + +        @Override +        public void onPackagesAvailable(String[] packageNames, UserHandle user, +                boolean replacing) { +        } + +        @Override +        public void onPackagesUnavailable(String[] packageNames, UserHandle user, +                boolean replacing) { +        } + +        @Override +        public void onShortcutsChanged(@NonNull String packageName, +                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { +            HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName); +            ArrayList<String> bubbleKeysToRemove = new ArrayList<>(); +            if (shortcutBubbles != null) { +                // If we can't find one of our bubbles in the shortcut list, that bubble needs +                // to be removed. +                for (String shortcutId : shortcutBubbles.keySet()) { +                    boolean foundShortcut = false; +                    for (int i = 0; i < shortcuts.size(); i++) { +                        if (shortcuts.get(i).getId().equals(shortcutId)) { +                            foundShortcut = true; +                            break; +                        } +                    } +                    if (!foundShortcut) { +                        bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId)); +                    } +                } +            } + +            // Let NoMan know about the updates +            for (int i = 0; i < bubbleKeysToRemove.size(); i++) { +                // update flag bubble +                String bubbleKey = bubbleKeysToRemove.get(i); +                if (mShortcutListener != null) { +                    mShortcutListener.onShortcutRemoved(bubbleKey); +                } +            } +        } +    }; + +    ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener) { +        mLauncherAppsService = launcherApps; +        mShortcutListener = listener; +    } + +    @VisibleForTesting +    void setLauncherApps(LauncherApps launcherApps) { +        mLauncherAppsService = launcherApps; +    } + +    ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) { +        if (mLauncherAppsService == null) { +            return null; +        } +        final long token = Binder.clearCallingIdentity(); +        try { +            if (shortcutId == null || packageName == null || user == null) { +                return null; +            } +            LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); +            query.setPackage(packageName); +            query.setShortcutIds(Arrays.asList(shortcutId)); +            query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED); +            List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user); +            return shortcuts != null && shortcuts.size() > 0 +                    ? shortcuts.get(0) +                    : null; +        } finally { +            Binder.restoreCallingIdentity(token); +        } +    } + +    boolean hasValidShortcutInfo(String shortcutId, String packageName, +            UserHandle user) { +        ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user); +        return shortcutInfo != null && shortcutInfo.isLongLived(); +    } + +    /** +     * Shortcut based bubbles require some extra work to listen for shortcut changes. +     * +     * @param r the notification record to check +     * @param removedNotification true if this notification is being removed +     * @param handler handler to register the callback with +     */ +    void maybeListenForShortcutChangesForBubbles(NotificationRecord r, boolean removedNotification, +            Handler handler) { +        final String shortcutId = r.getNotification().getBubbleMetadata() != null +                ? r.getNotification().getBubbleMetadata().getShortcutId() +                : null; +        if (shortcutId == null) { +            return; +        } +        if (r.getNotification().isBubbleNotification() && !removedNotification) { +            // Must track shortcut based bubbles in case the shortcut is removed +            HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get( +                    r.getSbn().getPackageName()); +            if (packageBubbles == null) { +                packageBubbles = new HashMap<>(); +            } +            packageBubbles.put(shortcutId, r.getKey()); +            mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles); +            if (!mLauncherAppsCallbackRegistered) { +                mLauncherAppsService.registerCallback(mLauncherAppsCallback, handler); +                mLauncherAppsCallbackRegistered = true; +            } +        } else { +            // No longer track shortcut +            HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get( +                    r.getSbn().getPackageName()); +            if (packageBubbles != null) { +                packageBubbles.remove(shortcutId); +            } +            if (packageBubbles != null && packageBubbles.isEmpty()) { +                mActiveShortcutBubbles.remove(r.getSbn().getPackageName()); +            } +            if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) { +                mLauncherAppsService.unregisterCallback(mLauncherAppsCallback); +                mLauncherAppsCallbackRegistered = false; +            } +        } +    } +} diff --git a/services/core/java/com/android/server/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 61bf5f0b6dfd..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 @@ -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/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 64edacd438fc..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: 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 7f7d668ea8ac..fd275d83ba56 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -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); @@ -3006,11 +3044,7 @@ public class StatsPullAtomService extends SystemService {          if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {              e.writeString(attributionTag);          } -        if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { -            e.writeString(op.getOpName()); -        } else { -            e.writeInt(op.getOpCode()); -        } +        e.writeInt(op.getLoggingOpCode());          e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));          e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));          e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED)); 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/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index b43d8b7daf4a..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; @@ -40,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; @@ -70,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);      } @@ -96,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");              } @@ -120,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); @@ -132,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);              } @@ -140,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");              } @@ -151,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 @@ -160,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] + ")"); @@ -169,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();                  } @@ -185,6 +213,8 @@ 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);              } @@ -192,25 +222,34 @@ public class TunerResourceManagerService extends SystemService {          @Override          public boolean requestDemux(@NonNull TunerDemuxRequest request, -                    @NonNull int[] demuxHandle) { -            if (DEBUG) { -                Slog.d(TAG, "requestDemux(request=" + 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);              } -            return true;          }          @Override          public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, -                    @NonNull int[] descrambleHandle) { -            if (DEBUG) { -                Slog.d(TAG, "requestDescrambler(request=" + 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);              } -            return true;          }          @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 + ")");              } @@ -219,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 + ")");              } @@ -228,6 +269,8 @@ public class TunerResourceManagerService extends SystemService {          @Override          public void releaseFrontend(int frontendId) { +            enforceTunerAccessPermission("releaseFrontend"); +            enforceTrmAccessPermission("releaseFrontend");              if (DEBUG) {                  Slog.d(TAG, "releaseFrontend(id=" + frontendId + ")");              } @@ -235,6 +278,8 @@ public class TunerResourceManagerService extends SystemService {          @Override          public void releaseDemux(int demuxHandle) { +            enforceTunerAccessPermission("releaseDemux"); +            enforceTrmAccessPermission("releaseDemux");              if (DEBUG) {                  Slog.d(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")");              } @@ -242,6 +287,8 @@ public class TunerResourceManagerService extends SystemService {          @Override          public void releaseDescrambler(int descramblerHandle) { +            enforceTunerAccessPermission("releaseDescrambler"); +            enforceTrmAccessPermission("releaseDescrambler");              if (DEBUG) {                  Slog.d(TAG, "releaseDescrambler(descramblerHandle=" + descramblerHandle + ")");              } @@ -249,6 +296,7 @@ public class TunerResourceManagerService extends SystemService {          @Override          public void releaseCasSession(int sessionResourceId) { +            enforceTrmAccessPermission("releaseCasSession");              if (DEBUG) {                  Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")");              } @@ -256,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 + ")");              } @@ -264,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 @@ -371,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; @@ -413,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;          } @@ -431,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; @@ -592,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/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e73c928ef925..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(); @@ -6362,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);              }          } @@ -6400,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.       */ @@ -6507,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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 98e3d07fc2e4..aad242dbc338 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -375,7 +375,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo       */      final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics(); -    /** @see #computeCompatSmallestWidth(boolean, int, int, int, DisplayCutout) */ +    /** @see #computeCompatSmallestWidth(boolean, int, int, int) */      private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();      /** @@ -1814,7 +1814,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo          final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);          outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw, -                dh, displayCutout); +                dh);      }      /** @@ -1922,8 +1922,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo          mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);      } -    private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh, -            DisplayCutout displayCutout) { +    private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh) {          mTmpDisplayMetrics.setTo(mDisplayMetrics);          final DisplayMetrics tmpDm = mTmpDisplayMetrics;          final int unrotDw, unrotDh; @@ -1934,19 +1933,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo              unrotDw = dw;              unrotDh = dh;          } -        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh, -                displayCutout); -        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw, -                displayCutout); -        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh, -                displayCutout); -        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw, -                displayCutout); +        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, +                unrotDh); +        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, +                unrotDw); +        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, +                unrotDh); +        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, +                unrotDw);          return sw;      }      private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode, -            DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout) { +            DisplayMetrics dm, int dw, int dh) { +        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation( +                rotation).getDisplayCutout();          dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,                  displayCutout);          dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, @@ -1987,20 +1988,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo              return;          }          int sl = Configuration.resetScreenLayout(outConfig.screenLayout); -        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode, -                displayInfo.displayCutout); -        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode, -                displayInfo.displayCutout); -        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode, -                displayInfo.displayCutout); -        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode, -                displayInfo.displayCutout); +        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode); +        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode); +        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode); +        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);          outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);          outConfig.screenLayout = sl;      }      private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh, -            int uiMode, DisplayCutout displayCutout) { +            int uiMode) { +        // Get the display cutout at this rotation. +        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation( +                rotation).getDisplayCutout(); +          // Get the app screen size at this rotation.          int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);          int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout); @@ -6523,6 +6524,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 958c8af3af24..30912e55f908 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -409,6 +409,13 @@ class InsetsPolicy {                  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, diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index fc358ce7675f..bd5666dd9a27 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -846,10 +846,9 @@ class RecentTasks {      @VisibleForTesting      Set<Integer> getProfileIds(int userId) {          Set<Integer> userIds = new ArraySet<>(); -        final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId, -                false /* enabledOnly */); -        for (int i = profiles.size() - 1; i >= 0; --i) { -            userIds.add(profiles.get(i).id); +        int[] profileIds = mService.getUserManager().getProfileIds(userId, false /* enabledOnly */); +        for (int i = 0; i < profileIds.length; i++) { +            userIds.add(Integer.valueOf(profileIds[i]));          }          return userIds;      } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 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 5ab5fbcf693d..e78f2ee47d65 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2127,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 */); @@ -2149,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);      } @@ -3370,6 +3386,9 @@ class Task extends WindowContainer<WindowContainer> {          } else {              info.pictureInPictureParams = mPictureInPictureParams;          } +        info.topActivityInfo = mReuseActivitiesReport.top != null +                ? mReuseActivitiesReport.top.info +                : null;      }      /** 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/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 f29f8f9e1cd7..36175705b848 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2938,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);          } @@ -2947,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 */);          }      } @@ -2962,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);              } 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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6f41631a8b88..f5d2c6a5c329 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -786,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. @@ -3541,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); @@ -3667,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) { @@ -3769,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)); @@ -3804,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);          } @@ -3830,7 +3831,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {          }      } -    private void updateProtectedPackagesLocked(List<String> packages) { +    private void updateUserControlDisabledPackagesLocked(List<String> packages) {          mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages);      } @@ -8830,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 { @@ -9584,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();          } @@ -15559,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;          }      } 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 053a7984b1a3..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); @@ -172,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/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 7571f0916974..d038d6c1ca7f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5843,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"); @@ -5851,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"); @@ -5867,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/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java new file mode 100644 index 000000000000..9636342c3f41 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.notification; + +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; + +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BubbleCheckerTest extends UiServiceTestCase { + +    private static final String SHORTCUT_ID = "shortcut"; +    private static final String PKG = "pkg"; +    private static final String KEY = "key"; +    private static final int USER_ID = 1; + +    @Mock +    ActivityManager mActivityManager; +    @Mock +    RankingConfig mRankingConfig; +    @Mock +    ShortcutHelper mShortcutHelper; + +    @Mock +    NotificationRecord mNr; +    @Mock +    UserHandle mUserHandle; +    @Mock +    Notification mNotif; +    @Mock +    StatusBarNotification mSbn; +    @Mock +    NotificationChannel mChannel; +    @Mock +    Notification.BubbleMetadata mBubbleMetadata; +    @Mock +    PendingIntent mPendingIntent; +    @Mock +    Intent mIntent; + +    BubbleExtractor.BubbleChecker mBubbleChecker; + +    @Before +    public void setUp() { +        MockitoAnnotations.initMocks(this); + +        when(mNr.getKey()).thenReturn(KEY); +        when(mNr.getSbn()).thenReturn(mSbn); +        when(mNr.getUser()).thenReturn(mUserHandle); +        when(mUserHandle.getIdentifier()).thenReturn(USER_ID); +        when(mNr.getChannel()).thenReturn(mChannel); +        when(mSbn.getPackageName()).thenReturn(PKG); +        when(mSbn.getUser()).thenReturn(mUserHandle); +        when(mNr.getNotification()).thenReturn(mNotif); +        when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata); + +        mBubbleChecker = new BubbleExtractor.BubbleChecker(mContext, +                mShortcutHelper, +                mRankingConfig, +                mActivityManager); +    } + +    void setUpIntentBubble() { +        when(mPendingIntent.getIntent()).thenReturn(mIntent); +        when(mBubbleMetadata.getBubbleIntent()).thenReturn(mPendingIntent); +        when(mBubbleMetadata.getShortcutId()).thenReturn(null); +    } + +    void setUpShortcutBubble(boolean isValid) { +        when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID); +        when(mShortcutHelper.hasValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle)) +                .thenReturn(isValid); +        when(mBubbleMetadata.getBubbleIntent()).thenReturn(null); +    } + +    void setUpBubblesEnabled(boolean feature, boolean app, boolean channel) { +        when(mRankingConfig.bubblesEnabled()).thenReturn(feature); +        when(mRankingConfig.areBubblesAllowed(PKG, USER_ID)).thenReturn(app); +        when(mChannel.canBubble()).thenReturn(channel); +    } + +    void setUpActivityIntent(boolean isResizable) { +        when(mPendingIntent.getIntent()).thenReturn(mIntent); +        ActivityInfo info = new ActivityInfo(); +        info.resizeMode = isResizable +                ? RESIZE_MODE_RESIZEABLE +                : RESIZE_MODE_UNRESIZEABLE; +        when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(info); +    } + +    // +    // canBubble +    // + +    @Test +    public void testCanBubble_true_intentBubble() { +        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */); +        setUpIntentBubble(); +        setUpActivityIntent(true /* isResizable */); +        when(mActivityManager.isLowRamDevice()).thenReturn(false); +        assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID)); +    } + +    @Test +    public void testCanBubble_true_shortcutBubble() { +        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */); +        setUpShortcutBubble(true /* isValid */); +        assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID)); +    } + +    @Test +    public void testCanBubble_false_noIntentInvalidShortcut() { +        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */); +        setUpShortcutBubble(false /* isValid */); +        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID)); +    } + +    @Test +    public void testCanBubble_false_noIntentNoShortcut() { +        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */); +        when(mBubbleMetadata.getBubbleIntent()).thenReturn(null); +        when(mBubbleMetadata.getShortcutId()).thenReturn(null); +        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID)); +    } + +    @Test +    public void testCanBubbble_false_noMetadata() { +        setUpBubblesEnabled(true/* feature */, true /* app */, true /* channel */); +        when(mNotif.getBubbleMetadata()).thenReturn(null); +        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID)); +    } + +    @Test +    public void testCanBubble_false_bubblesNotEnabled() { +        setUpBubblesEnabled(false /* feature */, true /* app */, true /* channel */); +        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID)); +    } + +    @Test +    public void testCanBubble_false_packageNotAllowed() { +        setUpBubblesEnabled(true /* feature */, false /* app */, true /* channel */); +        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID)); +    } + +    @Test +    public void testCanBubble_false_channelNotAllowed() { +        setUpBubblesEnabled(true /* feature */, true /* app */, false /* channel */); +        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID)); +    } + +    // +    // canLaunchInActivityView +    // + +    @Test +    public void testCanLaunchInActivityView_true() { +        setUpActivityIntent(true /* resizable */); +        assertTrue(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG)); +    } + +    @Test +    public void testCanLaunchInActivityView_false_noIntent() { +        when(mPendingIntent.getIntent()).thenReturn(null); +        assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG)); +    } + +    @Test +    public void testCanLaunchInActivityView_false_noInfo() { +        when(mPendingIntent.getIntent()).thenReturn(mIntent); +        when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(null); +        assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG)); +    } + +    @Test +    public void testCanLaunchInActivityView_false_notResizable() { +        setUpActivityIntent(false  /* resizable */); +        assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG)); +    } + +    // +    // isNotificationAppropriateToBubble +    // + +    @Test +    public void testIsNotifAppropriateToBubble_true() { +        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */); +        setUpIntentBubble(); +        when(mActivityManager.isLowRamDevice()).thenReturn(false); +        setUpActivityIntent(true /* resizable */); +        doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle(); + +        assertTrue(mBubbleChecker.isNotificationAppropriateToBubble(mNr)); +    } + +    @Test +    public void testIsNotifAppropriateToBubble_false_lowRam() { +        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */); +        when(mActivityManager.isLowRamDevice()).thenReturn(true); +        setUpActivityIntent(true /* resizable */); +        doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle(); + +        assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr)); +    } + +    @Test +    public void testIsNotifAppropriateToBubble_false_notMessageStyle() { +        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */); +        when(mActivityManager.isLowRamDevice()).thenReturn(false); +        setUpActivityIntent(true /* resizable */); +        doReturn(Notification.BigPictureStyle.class).when(mNotif).getNotificationStyle(); + +        assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr)); +    } + +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java index 7459c4b0610e..c7cef05ce442 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java @@ -21,6 +21,7 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;  import static junit.framework.Assert.assertFalse;  import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.mock;  import static org.mockito.Mockito.when;  import android.app.ActivityManager; @@ -46,6 +47,7 @@ import org.mockito.MockitoAnnotations;  public class BubbleExtractorTest extends UiServiceTestCase {      @Mock RankingConfig mConfig; +    BubbleExtractor mBubbleExtractor;      private String mPkg = "com.android.server.notification";      private int mId = 1001; @@ -57,6 +59,10 @@ public class BubbleExtractorTest extends UiServiceTestCase {      @Before      public void setUp() {          MockitoAnnotations.initMocks(this); +        mBubbleExtractor = new BubbleExtractor(); +        mBubbleExtractor.initialize(mContext, mock(NotificationUsageStats.class)); +        mBubbleExtractor.setConfig(mConfig); +        mBubbleExtractor.setShortcutHelper(mock(ShortcutHelper.class));      }      private NotificationRecord getNotificationRecord(boolean allow, int importanceHigh) { @@ -83,70 +89,55 @@ public class BubbleExtractorTest extends UiServiceTestCase {      @Test      public void testAppYesChannelNo() { -        BubbleExtractor extractor = new BubbleExtractor(); -        extractor.setConfig(mConfig); -          when(mConfig.bubblesEnabled()).thenReturn(true);          when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);          NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED); -        extractor.process(r); +        mBubbleExtractor.process(r);          assertFalse(r.canBubble());      }      @Test      public void testAppNoChannelYes() throws Exception { -        BubbleExtractor extractor = new BubbleExtractor(); -        extractor.setConfig(mConfig); -          when(mConfig.bubblesEnabled()).thenReturn(true);          when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);          NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH); -        extractor.process(r); +        mBubbleExtractor.process(r);          assertFalse(r.canBubble());      }      @Test      public void testAppYesChannelYes() { -        BubbleExtractor extractor = new BubbleExtractor(); -        extractor.setConfig(mConfig); -          when(mConfig.bubblesEnabled()).thenReturn(true);          when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);          NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED); -        extractor.process(r); +        mBubbleExtractor.process(r);          assertTrue(r.canBubble());      }      @Test      public void testAppNoChannelNo() { -        BubbleExtractor extractor = new BubbleExtractor(); -        extractor.setConfig(mConfig); -          when(mConfig.bubblesEnabled()).thenReturn(true);          when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);          NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED); -        extractor.process(r); +        mBubbleExtractor.process(r);          assertFalse(r.canBubble());      }      @Test      public void testAppYesChannelYesUserNo() { -        BubbleExtractor extractor = new BubbleExtractor(); -        extractor.setConfig(mConfig); -          when(mConfig.bubblesEnabled()).thenReturn(false);          when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);          NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH); -        extractor.process(r); +        mBubbleExtractor.process(r);          assertFalse(r.canBubble());      } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index dc8d0104353a..8e7804786b38 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -35,7 +35,6 @@ import android.app.INotificationManager;  import android.app.Notification;  import android.app.NotificationChannel;  import android.app.PendingIntent; -import android.app.Person;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent; @@ -191,7 +190,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {                  tweak.canBubble(),                  tweak.visuallyInterruptive(),                  tweak.isConversation(), -                tweak.getShortcutInfo() +                tweak.getShortcutInfo(), +                tweak.isBubble()          );          assertNotEquals(nru, nru2);      } @@ -270,7 +270,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {                      canBubble(i),                      visuallyInterruptive(i),                      isConversation(i), -                    getShortcutInfo(i) +                    getShortcutInfo(i), +                    isBubble(i)              );              rankings[i] = ranking;          } @@ -394,6 +395,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {          return si;      } +    private boolean isBubble(int index) { +        return index % 4 == 0; +    } +      private void assertActionsEqual(              List<Notification.Action> expecteds, List<Notification.Action> actuals) {          assertEquals(expecteds.size(), actuals.size()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 64d481a2e6a0..f179840fba11 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; @@ -204,6 +202,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {      private TestableNotificationManagerService mService;      private INotificationManager mBinderService;      private NotificationManagerInternal mInternalService; +    private TestableBubbleChecker mTestableBubbleChecker; +    private ShortcutHelper mShortcutHelper;      @Mock      private IPackageManager mPackageManager;      @Mock @@ -286,6 +286,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {              super(context, logger, notificationInstanceIdSequence);          } +        RankingHelper getRankingHelper() { +            return mRankingHelper; +        } +          @Override          protected boolean isCallingUidSystem() {              countSystemChecks++; @@ -337,6 +341,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {          interface NotificationAssistantAccessGrantedCallback {              void onGranted(ComponentName assistant, int userId, boolean granted);          } +    } + +    private class TestableBubbleChecker extends BubbleExtractor.BubbleChecker { + +        TestableBubbleChecker(Context context, ShortcutHelper helper, RankingConfig config, +                ActivityManager manager) { +            super(context, helper, config, manager); +        }          @Override          protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent, @@ -448,7 +460,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {          mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);          mService.setAudioManager(mAudioManager); -        mService.setLauncherApps(mLauncherApps); + +        mShortcutHelper = mService.getShortcutHelper(); +        mShortcutHelper.setLauncherApps(mLauncherApps); + +        // Set the testable bubble extractor +        RankingHelper rankingHelper = mService.getRankingHelper(); +        BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class); +        mTestableBubbleChecker = new TestableBubbleChecker(mContext, mShortcutHelper, +                mService.mPreferencesHelper, mActivityManager); +        extractor.setBubbleChecker(mTestableBubbleChecker);          // Tests call directly into the Binder.          mBinderService = mService.getBinderService(); @@ -462,6 +483,15 @@ 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 +1180,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 +1209,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 +1233,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 +1252,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 +1271,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 +1299,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 +2557,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 +2606,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 +2631,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 +2658,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 +3448,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 +3470,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 +3537,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 +4423,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 +4434,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 +4860,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 +4884,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 +4906,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 +5574,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 @@ -5710,6 +5799,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {      @Test      public void testOnBubbleNotificationSuppressionChanged() throws Exception { +        // Bubbles are allowed! +        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */); +          // Bubble notification          NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag"); @@ -6111,8 +6203,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {          assertTrue(notif.isBubbleNotification());          // Test: Remove the shortcut +        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);          launcherAppsCallback.getValue().onShortcutsChanged(PKG, Collections.emptyList(),                  new UserHandle(mUid)); +        waitForIdle();          // Verify: @@ -6125,6 +6219,58 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {          assertFalse(notif2.isBubbleNotification());      } + +    @Test +    public void testNotificationBubbles_shortcut_stopListeningWhenNotifRemoved() +            throws RemoteException { +        // Bubbles are allowed! +        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */); + +        ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback = +                ArgumentCaptor.forClass(LauncherApps.Callback.class); + +        // Messaging notification with shortcut info +        Notification.BubbleMetadata metadata = +                getBubbleMetadataBuilder().createShortcutBubble("someshortcutId").build(); +        Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */, +                null /* groupKey */, false /* isSummary */); +        nb.setBubbleMetadata(metadata); +        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, +                "tag", mUid, 0, nb.build(), new UserHandle(mUid), null, 0); +        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + +        // Pretend the shortcut exists +        List<ShortcutInfo> shortcutInfos = new ArrayList<>(); +        ShortcutInfo info = mock(ShortcutInfo.class); +        when(info.isLongLived()).thenReturn(true); +        shortcutInfos.add(info); +        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos); + +        // Test: Send the bubble notification +        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), +                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); +        waitForIdle(); + +        // Verify: + +        // Make sure we register the callback for shortcut changes +        verify(mLauncherApps, times(1)).registerCallback(launcherAppsCallback.capture(), any()); + +        // yes allowed, yes messaging w/shortcut, yes bubble +        Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification(); +        assertTrue(notif.isBubbleNotification()); + +        // Test: Remove the notification +        mBinderService.cancelNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), +                nr.getSbn().getId(), nr.getSbn().getUserId()); +        waitForIdle(); + +        // Verify: + +        // Make sure callback is unregistered +        verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue()); +    } +      @Test      public void testNotificationBubbles_bubbleChildrenStay_whenGroupSummaryDismissed()              throws Exception { @@ -6168,6 +6314,17 @@ 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 +6345,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/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java new file mode 100644 index 000000000000..50fb9b425652 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.notification; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutInfo; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.TestableLooper; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +@TestableLooper.RunWithLooper +public class ShortcutHelperTest extends UiServiceTestCase { + +    private static final String SHORTCUT_ID = "shortcut"; +    private static final String PKG = "pkg"; +    private static final String KEY = "key"; + +    @Mock +    LauncherApps mLauncherApps; +    @Mock +    ShortcutHelper.ShortcutListener mShortcutListener; +    @Mock +    NotificationRecord mNr; +    @Mock +    Notification mNotif; +    @Mock +    StatusBarNotification mSbn; +    @Mock +    Notification.BubbleMetadata mBubbleMetadata; + +    ShortcutHelper mShortcutHelper; + +    @Before +    public void setUp() { +        MockitoAnnotations.initMocks(this); + +        mShortcutHelper = new ShortcutHelper(mLauncherApps, mShortcutListener); +        when(mNr.getKey()).thenReturn(KEY); +        when(mNr.getSbn()).thenReturn(mSbn); +        when(mSbn.getPackageName()).thenReturn(PKG); +        when(mNr.getNotification()).thenReturn(mNotif); +        when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata); +        when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID); +    } + +    private LauncherApps.Callback addShortcutBubbleAndVerifyListener() { +        when(mNotif.isBubbleNotification()).thenReturn(true); + +        mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr, +                false /* removed */, +                null /* handler */); + +        ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback = +                ArgumentCaptor.forClass(LauncherApps.Callback.class); + +        verify(mLauncherApps, times(1)).registerCallback( +                launcherAppsCallback.capture(), any()); +        return launcherAppsCallback.getValue(); +    } + +    @Test +    public void testBubbleAdded_listenedAdded() { +        addShortcutBubbleAndVerifyListener(); +    } + +    @Test +    public void testBubbleRemoved_listenerRemoved() { +        // First set it up to listen +        addShortcutBubbleAndVerifyListener(); + +        // Then remove the notif +        mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr, +                true /* removed */, +                null /* handler */); + +        verify(mLauncherApps, times(1)).unregisterCallback(any()); +    } + +    @Test +    public void testBubbleNoLongerBubble_listenerRemoved() { +        // First set it up to listen +        addShortcutBubbleAndVerifyListener(); + +        // Then make it not a bubble +        when(mNotif.isBubbleNotification()).thenReturn(false); +        mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr, +                false /* removed */, +                null /* handler */); + +        verify(mLauncherApps, times(1)).unregisterCallback(any()); +    } + +    @Test +    public void testListenerNotifiedOnShortcutRemoved() { +        LauncherApps.Callback callback = addShortcutBubbleAndVerifyListener(); + +        List<ShortcutInfo> shortcutInfos = new ArrayList<>(); +        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos); + +        callback.onShortcutsChanged(PKG, shortcutInfos, mock(UserHandle.class)); +        verify(mShortcutListener).onShortcutRemoved(mNr.getKey()); +    } +} diff --git a/services/tests/wmtests/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/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/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/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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 08251da609e8..3f106f7c3343 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -5280,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 { @@ -9189,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; @@ -9218,7 +9217,6 @@ public class TelephonyManager {       *       * @hide       */ -    @SystemApi      @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)      public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) {          try { @@ -9244,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 */ @@ -9275,7 +9270,6 @@ public class TelephonyManager {       *       * @hide       */ -    @SystemApi      @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)      public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) {          try { @@ -12463,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) { @@ -12494,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, @@ -12525,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, @@ -12889,7 +12880,6 @@ public class TelephonyManager {       *       * @hide       */ -    @SystemApi      @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)      public boolean setDataAllowedDuringVoiceCall(boolean allow) {          try { @@ -12918,7 +12908,6 @@ public class TelephonyManager {       *       * @hide       */ -    @SystemApi      @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)      public boolean isDataAllowedInVoiceCall() {          try { @@ -12965,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;      /** @@ -12978,7 +12966,6 @@ public class TelephonyManager {       *       * @hide       */ -    @SystemApi      @WorkerThread      @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)      public boolean isIccLockEnabled() { @@ -13015,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."); @@ -13049,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/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/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/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/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 70542b5d3c65..727952c1938c 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -695,31 +695,6 @@ public class ScanResult implements Parcelable {       */      public AnqpInformationElement[] anqpElements; -    /** -     * Flag indicating if this AP is a carrier AP. The determination is based -     * on the AP's SSID and if AP is using EAP security. -     * -     * @hide -     */ -    // TODO(b/144431927): remove once migrated to Suggestions -    public boolean isCarrierAp; - -    /** -     * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP. -     * -     * @hide -     */ -    // TODO(b/144431927): remove once migrated to Suggestions -    public int carrierApEapType; - -    /** -     * The name of the carrier that's associated with this AP if it is a carrier AP. -     * -     * @hide -     */ -    // TODO(b/144431927): remove once migrated to Suggestions -    public String carrierName; -      /** {@hide} */      public ScanResult(WifiSsid wifiSsid, String BSSID, long hessid, int anqpDomainId,              byte[] osuProviders, String caps, int level, int frequency, long tsf) { @@ -744,9 +719,6 @@ public class ScanResult implements Parcelable {          this.centerFreq0 = UNSPECIFIED;          this.centerFreq1 = UNSPECIFIED;          this.flags = 0; -        this.isCarrierAp = false; -        this.carrierApEapType = UNSPECIFIED; -        this.carrierName = null;          this.radioChainInfos = null;          this.mWifiStandard = WIFI_STANDARD_UNKNOWN;      } @@ -767,9 +739,6 @@ public class ScanResult implements Parcelable {          this.centerFreq0 = UNSPECIFIED;          this.centerFreq1 = UNSPECIFIED;          this.flags = 0; -        this.isCarrierAp = false; -        this.carrierApEapType = UNSPECIFIED; -        this.carrierName = null;          this.radioChainInfos = null;          this.mWifiStandard = WIFI_STANDARD_UNKNOWN;      } @@ -797,9 +766,6 @@ public class ScanResult implements Parcelable {          } else {              this.flags = 0;          } -        this.isCarrierAp = false; -        this.carrierApEapType = UNSPECIFIED; -        this.carrierName = null;          this.radioChainInfos = null;          this.mWifiStandard = WIFI_STANDARD_UNKNOWN;      } @@ -839,9 +805,6 @@ public class ScanResult implements Parcelable {              venueName = source.venueName;              operatorFriendlyName = source.operatorFriendlyName;              flags = source.flags; -            isCarrierAp = source.isCarrierAp; -            carrierApEapType = source.carrierApEapType; -            carrierName = source.carrierName;              radioChainInfos = source.radioChainInfos;              this.mWifiStandard = source.mWifiStandard;          } @@ -881,9 +844,6 @@ public class ScanResult implements Parcelable {          sb.append(", standard: ").append(wifiStandardToString(mWifiStandard));          sb.append(", 80211mcResponder: ");          sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported"); -        sb.append(", Carrier AP: ").append(isCarrierAp ? "yes" : "no"); -        sb.append(", Carrier AP EAP Type: ").append(carrierApEapType); -        sb.append(", Carrier name: ").append(carrierName);          sb.append(", Radio Chain Infos: ").append(Arrays.toString(radioChainInfos));          return sb.toString();      } @@ -954,9 +914,6 @@ public class ScanResult implements Parcelable {          } else {              dest.writeInt(0);          } -        dest.writeInt(isCarrierAp ? 1 : 0); -        dest.writeInt(carrierApEapType); -        dest.writeString(carrierName);          if (radioChainInfos != null) {              dest.writeInt(radioChainInfos.length); @@ -1036,9 +993,6 @@ public class ScanResult implements Parcelable {                                  new AnqpInformationElement(vendorId, elementId, payload);                      }                  } -                sr.isCarrierAp = in.readInt() != 0; -                sr.carrierApEapType = in.readInt(); -                sr.carrierName = in.readString();                  n = in.readInt();                  if (n != 0) {                      sr.radioChainInfos = new RadioChainInfo[n]; diff --git a/wifi/java/android/net/wifi/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/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/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java index 4c22d5d6dc7e..6cb832463bc0 100644 --- a/wifi/tests/src/android/net/wifi/ScanResultTest.java +++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java @@ -156,8 +156,7 @@ public class ScanResultTest {                  + "distance: 0(cm), distanceSd: 0(cm), "                  + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "                  + "standard: 11ac, " -                + "80211mcResponder: is not supported, Carrier AP: no, " -                + "Carrier AP EAP Type: 0, Carrier name: null, " +                + "80211mcResponder: is not supported, "                  + "Radio Chain Infos: null", scanResult.toString());      } @@ -179,8 +178,7 @@ public class ScanResultTest {                  + "distanceSd: 0(cm), "                  + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "                  + "standard: 11ac, " -                + "80211mcResponder: is not supported, Carrier AP: no, " -                + "Carrier AP EAP Type: 0, Carrier name: null, " +                + "80211mcResponder: is not supported, "                  + "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, "                  + "RadioChainInfo: id=1, level=-54]", scanResult.toString());      } diff --git a/wifi/tests/src/android/net/wifi/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/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()); +    }  }  |