diff options
127 files changed, 2385 insertions, 1601 deletions
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index b3c82bc6bebf..073fddf8f615 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -850,7 +850,7 @@ public final class MediaParser { private final InputReadingDataSource mDataSource; private final DataReaderAdapter mScratchDataReaderAdapter; private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter; - private String mExtractorName; + private String mParserName; private Extractor mExtractor; private ExtractorInput mExtractorInput; private long mPendingSeekPosition; @@ -924,7 +924,7 @@ public final class MediaParser { @NonNull @ParserName public String getParserName() { - return mExtractorName; + return mParserName; } /** @@ -958,15 +958,15 @@ public final class MediaParser { // TODO: Apply parameters when creating extractor instances. if (mExtractor == null) { - if (!mExtractorName.equals(PARSER_NAME_UNKNOWN)) { - mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance(); + if (!mParserName.equals(PARSER_NAME_UNKNOWN)) { + mExtractor = createExtractor(mParserName); mExtractor.init(new ExtractorOutputAdapter()); } else { for (String parserName : mParserNamesPool) { Extractor extractor = createExtractor(parserName); try { if (extractor.sniff(mExtractorInput)) { - mExtractorName = parserName; + mParserName = parserName; mExtractor = extractor; mExtractor.init(new ExtractorOutputAdapter()); break; @@ -1044,7 +1044,7 @@ public final class MediaParser { mParserParameters = new HashMap<>(); mOutputConsumer = outputConsumer; mParserNamesPool = parserNamesPool; - mExtractorName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0]; + mParserName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0]; mPositionHolder = new PositionHolder(); mDataSource = new InputReadingDataSource(); removePendingSeek(); @@ -1090,7 +1090,7 @@ public final class MediaParser { getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS) ? Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS : 0; - return new Mp4Extractor(); + return new Mp4Extractor(flags); case PARSER_NAME_MP3: flags |= getBooleanParameter(PARAMETER_MP3_DISABLE_ID3) diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 9ca2db970eb7..d36d583559a0 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3158,6 +3158,23 @@ public abstract class PackageManager { "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE"; /** + * Extra field name for the Merkle tree root hash of a package. + * <p>Passed to a package verifier both prior to verification and as a result + * of verification. + * <p>The value of the extra is a specially formatted list: + * {@code filename1:HASH_1;filename2:HASH_2;...;filenameN:HASH_N} + * <p>The extra must include an entry for every APK within an installation. If + * a hash is not physically present, a hash value of {@code 0} will be used. + * <p>The root hash is generated using SHA-256, no salt with a 4096 byte block + * size. See the description of the + * <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#merkle-tree">fs-verity merkle-tree</a> + * for more details. + * @hide + */ + public static final String EXTRA_VERIFICATION_ROOT_HASH = + "android.content.pm.extra.EXTRA_VERIFICATION_ROOT_HASH"; + + /** * Extra field name for the ID of a intent filter pending verification. * Passed to an intent filter verifier and is used to call back to * {@link #verifyIntentFilter} diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 4c9553249a0c..2ee0ad67b108 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -34,6 +34,7 @@ import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; import android.os.Bundle; import android.util.SparseArray; +import android.util.SparseIntArray; import java.security.PublicKey; import java.util.Map; @@ -258,6 +259,8 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName); + ParsingPackage setMinExtensionVersions(@Nullable SparseIntArray minExtensionVersions); + ParsingPackage setMinSdkVersion(int minSdkVersion); ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index be1817d09155..1a1395ca7e9e 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -51,6 +51,7 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Pair; import android.util.SparseArray; +import android.util.SparseIntArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -340,6 +341,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { private String manageSpaceActivityName; private float maxAspectRatio; private float minAspectRatio; + @Nullable + private SparseIntArray minExtensionVersions; private int minSdkVersion; private int networkSecurityConfigRes; @Nullable @@ -1100,6 +1103,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeBoolean(this.preserveLegacyExternalStorage); dest.writeArraySet(this.mimeGroups); dest.writeInt(this.gwpAsanMode); + dest.writeSparseIntArray(this.minExtensionVersions); } public ParsingPackageImpl(Parcel in) { @@ -1259,6 +1263,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.preserveLegacyExternalStorage = in.readBoolean(); this.mimeGroups = (ArraySet<String>) in.readArraySet(boot); this.gwpAsanMode = in.readInt(); + this.minExtensionVersions = in.readSparseIntArray(); } public static final Parcelable.Creator<ParsingPackageImpl> CREATOR = @@ -1767,6 +1772,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { return minAspectRatio; } + @Nullable + @Override + public SparseIntArray getMinExtensionVersions() { + return minExtensionVersions; + } + @Override public int getMinSdkVersion() { return minSdkVersion; @@ -2215,6 +2226,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + public ParsingPackageImpl setMinExtensionVersions(@Nullable SparseIntArray value) { + minExtensionVersions = value; + return this; + } + + @Override public ParsingPackageImpl setMinSdkVersion(int value) { minSdkVersion = value; return this; diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index 687bc235cfa7..1ded8d40c727 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -41,6 +41,7 @@ import android.os.Parcelable; import android.util.ArraySet; import android.util.Pair; import android.util.SparseArray; +import android.util.SparseIntArray; import com.android.internal.R; @@ -609,6 +610,13 @@ public interface ParsingPackageRead extends Parcelable { String getManageSpaceActivityName(); /** + * @see ApplicationInfo#minExtensionVersions + * @see R.styleable#AndroidManifestExtensionSdk + */ + @Nullable + SparseIntArray getMinExtensionVersions(); + + /** * @see ApplicationInfo#minSdkVersion * @see R.styleable#AndroidManifestUsesSdk_minSdkVersion */ diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index c61362feaeae..29ece4924e5c 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -95,6 +95,7 @@ import android.util.DisplayMetrics; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.TypedValue; import android.util.apk.ApkSignatureVerifier; @@ -1255,6 +1256,7 @@ public class ParsingPackageUtils { int type; final int innerDepth = parser.getDepth(); + SparseIntArray minExtensionVersions = null; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { @@ -1263,7 +1265,10 @@ public class ParsingPackageUtils { final ParseResult result; if (parser.getName().equals("extension-sdk")) { - result = parseExtensionSdk(input, pkg, res, parser); + if (minExtensionVersions == null) { + minExtensionVersions = new SparseIntArray(); + } + result = parseExtensionSdk(input, res, parser, minExtensionVersions); XmlUtils.skipCurrentTag(parser); } else { result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input); @@ -1273,6 +1278,7 @@ public class ParsingPackageUtils { return input.error(result); } } + pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions)); } finally { sa.recycle(); } @@ -1280,8 +1286,21 @@ public class ParsingPackageUtils { return input.success(pkg); } - private static ParseResult parseExtensionSdk(ParseInput input, ParsingPackage pkg, - Resources res, XmlResourceParser parser) { + @Nullable + private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) { + if (input == null) { + return null; + } + SparseIntArray output = new SparseIntArray(input.size()); + for (int i = 0; i < input.size(); i++) { + output.put(input.keyAt(i), input.valueAt(i)); + } + return output; + } + + private static ParseResult<SparseIntArray> parseExtensionSdk( + ParseInput input, Resources res, XmlResourceParser parser, + SparseIntArray minExtensionVersions) { int sdkVersion; int minVersion; TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk); @@ -1316,7 +1335,8 @@ public class ParsingPackageUtils { PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Specified sdkVersion " + sdkVersion + " is not valid"); } - return input.success(pkg); + minExtensionVersions.put(sdkVersion, minVersion); + return input.success(minExtensionVersions); } /** diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java index 882a7f4ab37d..b3b4549426f0 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java @@ -233,8 +233,10 @@ public class LegacyFaceDetectMapper { Camera.Parameters params = legacyRequest.parameters; Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); - ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray, - request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params); + ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArray, + request.get(CaptureRequest.SCALER_CROP_REGION), + request.get(CaptureRequest.CONTROL_ZOOM_RATIO), + previewSize, params); List<Face> convertedFaces = new ArrayList<>(); if (faces != null) { diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java index 6953a5b793c3..362ddfae67bf 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -771,6 +771,7 @@ public class LegacyMetadataMapper { CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES , CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE , CameraCharacteristics.CONTROL_MAX_REGIONS , + CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE , CameraCharacteristics.FLASH_INFO_AVAILABLE , CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL , CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES , @@ -828,6 +829,7 @@ public class LegacyMetadataMapper { CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_SCENE_MODE, CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, + CaptureRequest.CONTROL_ZOOM_RATIO, CaptureRequest.FLASH_MODE, CaptureRequest.JPEG_GPS_COORDINATES, CaptureRequest.JPEG_GPS_PROCESSING_METHOD, @@ -872,6 +874,7 @@ public class LegacyMetadataMapper { CaptureResult.CONTROL_AWB_MODE , CaptureResult.CONTROL_AWB_LOCK , CaptureResult.CONTROL_MODE , + CaptureResult.CONTROL_ZOOM_RATIO , CaptureResult.FLASH_MODE , CaptureResult.JPEG_GPS_COORDINATES , CaptureResult.JPEG_GPS_PROCESSING_METHOD , @@ -935,6 +938,12 @@ public class LegacyMetadataMapper { private static void mapScaler(CameraMetadataNative m, Parameters p) { /* + * control.zoomRatioRange + */ + Range<Float> zoomRatioRange = new Range<Float>(1.0f, ParameterUtils.getMaxZoomRatio(p)); + m.set(CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange); + + /* * scaler.availableMaxDigitalZoom */ m.set(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, ParameterUtils.getMaxZoomRatio(p)); @@ -1383,6 +1392,9 @@ public class LegacyMetadataMapper { // control.sceneMode -- DISABLED is always available m.set(CaptureRequest.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED); + // control.zoomRatio -- 1.0 + m.set(CaptureRequest.CONTROL_ZOOM_RATIO, 1.0f); + /* * statistics.* */ diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java index 2e06d5fb3b6f..3a46379477e9 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java @@ -68,8 +68,9 @@ public class LegacyRequestMapper { */ ParameterUtils.ZoomData zoomData; { - zoomData = ParameterUtils.convertScalerCropRegion(activeArray, + zoomData = ParameterUtils.convertToLegacyZoom(activeArray, request.get(SCALER_CROP_REGION), + request.get(CONTROL_ZOOM_RATIO), previewSize, params); diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java index dc5823d80f21..09edf74f0d4c 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java @@ -118,8 +118,10 @@ public class LegacyResultMapper { Rect activeArraySize = characteristics.get( CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); - ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize, - request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params); + ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArraySize, + request.get(CaptureRequest.SCALER_CROP_REGION), + request.get(CaptureRequest.CONTROL_ZOOM_RATIO), + previewSize, params); /* * colorCorrection @@ -516,5 +518,12 @@ public class LegacyResultMapper { { m.set(SCALER_CROP_REGION, zoomData.reportedCrop); } + + /* + * control.zoomRatio + */ + { + m.set(CONTROL_ZOOM_RATIO, zoomData.reportedZoomRatio); + } } } diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java index 3cfd020aeee3..eb435989e9a0 100644 --- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java +++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java @@ -73,11 +73,15 @@ public class ParameterUtils { public final Rect previewCrop; /** Reported crop-region given the zoom index, coordinates relative to active-array */ public final Rect reportedCrop; + /** Reported zoom ratio given the zoom index */ + public final float reportedZoomRatio; - public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop) { + public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop, + float reportedZoomRatio) { this.zoomIndex = zoomIndex; this.previewCrop = previewCrop; this.reportedCrop = reportedCrop; + this.reportedZoomRatio = reportedZoomRatio; } } @@ -371,7 +375,8 @@ public class ParameterUtils { * @throws NullPointerException if any of the args were {@code null} */ public static int getClosestAvailableZoomCrop( - Camera.Parameters params, Rect activeArray, Size streamSize, Rect cropRegion, + Camera.Parameters params, Rect activeArray, + Size streamSize, Rect cropRegion, /*out*/ Rect reportedCropRegion, Rect previewCropRegion) { @@ -733,6 +738,92 @@ public class ParameterUtils { } /** + * Convert the user-specified crop region/zoom into zoom data; which can be used + * to set the parameters to a specific zoom index, or to report back to the user what + * the actual zoom was, or for other calculations requiring the current preview crop region. + * + * <p>None of the parameters are mutated.<p> + * + * @param activeArraySize active array size of the sensor (e.g. max jpeg size) + * @param cropRegion the user-specified crop region + * @param zoomRatio the user-specified zoom ratio + * @param previewSize the current preview size (in pixels) + * @param params the current camera parameters (not mutated) + * + * @return the zoom index, and the effective/reported crop regions (relative to active array) + */ + public static ZoomData convertToLegacyZoom(Rect activeArraySize, Rect + cropRegion, Float zoomRatio, Size previewSize, Camera.Parameters params) { + final float FLOAT_EQUAL_THRESHOLD = 0.0001f; + if (zoomRatio != null && + Math.abs(1.0f - zoomRatio) > FLOAT_EQUAL_THRESHOLD) { + // User uses CONTROL_ZOOM_RATIO to control zoom + return convertZoomRatio(activeArraySize, zoomRatio, previewSize, params); + } + + return convertScalerCropRegion(activeArraySize, cropRegion, previewSize, params); + } + + /** + * Convert the user-specified zoom ratio into zoom data; which can be used + * to set the parameters to a specific zoom index, or to report back to the user what the + * actual zoom was, or for other calculations requiring the current preview crop region. + * + * <p>None of the parameters are mutated.</p> + * + * @param activeArraySize active array size of the sensor (e.g. max jpeg size) + * @param zoomRatio the current zoom ratio + * @param previewSize the current preview size (in pixels) + * @param params the current camera parameters (not mutated) + * + * @return the zoom index, and the effective/reported crop regions (relative to active array) + */ + public static ZoomData convertZoomRatio(Rect activeArraySize, float zoomRatio, + Size previewSize, Camera.Parameters params) { + if (DEBUG) { + Log.v(TAG, "convertZoomRatio - user zoom ratio was " + zoomRatio); + } + + List<Rect> availableReportedCropRegions = + getAvailableZoomCropRectangles(params, activeArraySize); + List<Rect> availablePreviewCropRegions = + getAvailablePreviewZoomCropRectangles(params, activeArraySize, previewSize); + if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) { + throw new AssertionError("available reported/preview crop region size mismatch"); + } + + // Find the best matched legacy zoom ratio for the requested camera2 zoom ratio. + int bestZoomIndex = 0; + Rect reportedCropRegion = new Rect(availableReportedCropRegions.get(0)); + Rect previewCropRegion = new Rect(availablePreviewCropRegions.get(0)); + float reportedZoomRatio = 1.0f; + if (params.isZoomSupported()) { + List<Integer> zoomRatios = params.getZoomRatios(); + for (int i = 1; i < zoomRatios.size(); i++) { + if (zoomRatio * ZOOM_RATIO_MULTIPLIER >= zoomRatios.get(i)) { + bestZoomIndex = i; + reportedCropRegion = availableReportedCropRegions.get(i); + previewCropRegion = availablePreviewCropRegions.get(i); + reportedZoomRatio = zoomRatios.get(i); + } else { + break; + } + } + } + + if (DEBUG) { + Log.v(TAG, "convertZoomRatio - zoom calculated to: " + + "zoomIndex = " + bestZoomIndex + + ", reported crop region = " + reportedCropRegion + + ", preview crop region = " + previewCropRegion + + ", reported zoom ratio = " + reportedZoomRatio); + } + + return new ZoomData(bestZoomIndex, reportedCropRegion, + previewCropRegion, reportedZoomRatio); + } + + /** * Convert the user-specified crop region into zoom data; which can be used * to set the parameters to a specific zoom index, or to report back to the user what the * actual zoom was, or for other calculations requiring the current preview crop region. @@ -767,15 +858,17 @@ public class ParameterUtils { final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly, previewSize, userCropRegion, /*out*/reportedCropRegion, /*out*/previewCropRegion); + final float reportedZoomRatio = 1.0f; if (DEBUG) { Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " + "zoomIndex = " + zoomIdx + ", reported crop region = " + reportedCropRegion + - ", preview crop region = " + previewCropRegion); + ", preview crop region = " + previewCropRegion + + ", reported zoom ratio = " + reportedZoomRatio); } - return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion); + return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion, reportedZoomRatio); } /** diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java index 5cc73caa4f60..d35ce5b2c3f8 100644 --- a/core/java/android/os/incremental/V4Signature.java +++ b/core/java/android/os/incremental/V4Signature.java @@ -16,6 +16,8 @@ package android.os.incremental; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.ParcelFileDescriptor; import java.io.ByteArrayInputStream; @@ -45,8 +47,8 @@ public class V4Signature { 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 + @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max + @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) { this.hashAlgorithm = hashAlgorithm; @@ -58,7 +60,8 @@ public class V4Signature { /** * Constructs HashingInfo from byte array. */ - public static HashingInfo fromByteArray(byte[] bytes) throws IOException { + @NonNull + public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); final int hashAlgorithm = buffer.getInt(); final byte log2BlockSize = buffer.get(); @@ -106,8 +109,18 @@ public class V4Signature { } 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. + /** + * Raw byte array containing the IncFS hashing data. + * @see HashingInfo#fromByteArray(byte[]) + */ + @Nullable public final byte[] hashingInfo; + + /** + * Raw byte array containing the V4 signature data. + * <p>Passed as-is to the kernel. Can be retrieved later. + * @see SigningInfo#fromByteArray(byte[]) + */ + @Nullable public final byte[] signingInfo; /** * Construct a V4Signature from .idsig file. @@ -121,7 +134,8 @@ public class V4Signature { /** * Construct a V4Signature from a byte array. */ - public static V4Signature readFrom(byte[] bytes) throws IOException { + @NonNull + public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException { try (InputStream stream = new ByteArrayInputStream(bytes)) { return readFrom(stream); } @@ -169,7 +183,7 @@ public class V4Signature { return this.version == SUPPORTED_VERSION; } - private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) { + private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) { this.version = version; this.hashingInfo = hashingInfo; this.signingInfo = signingInfo; diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java index 63b380404217..9cf1b87f7eab 100644 --- a/core/java/android/service/autofill/InlinePresentation.java +++ b/core/java/android/service/autofill/InlinePresentation.java @@ -19,7 +19,6 @@ package android.service.autofill; import android.annotation.NonNull; import android.annotation.Size; import android.app.slice.Slice; -import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.widget.inline.InlinePresentationSpec; @@ -67,18 +66,6 @@ public final class InlinePresentation implements Parcelable { return hints.toArray(new String[hints.size()]); } - /** - * @hide - * @removed - */ - @UnsupportedAppUsage - public InlinePresentation( - @NonNull Slice slice, - @NonNull android.view.inline.InlinePresentationSpec inlinePresentationSpec, - boolean pinned) { - this(slice, inlinePresentationSpec.toWidget(), pinned); - } - // Code below generated by codegen v1.0.15. @@ -245,7 +232,7 @@ public final class InlinePresentation implements Parcelable { }; @DataClass.Generated( - time = 1585633564226L, + time = 1586992400667L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java", inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)") diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 3f0787350075..337027ef5bc9 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -182,7 +182,6 @@ public class DreamService extends Service implements Window.Callback { private Window mWindow; private Activity mActivity; private boolean mInteractive; - private boolean mLowProfile = true; private boolean mFullscreen; private boolean mScreenBright = true; private boolean mStarted; @@ -530,32 +529,6 @@ public class DreamService extends Service implements Window.Callback { } /** - * Sets View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. - * - * @param lowProfile True to set View.SYSTEM_UI_FLAG_LOW_PROFILE - * @hide There is no reason to have this -- dreams can set this flag - * on their own content view, and from there can actually do the - * correct interactions with it (seeing when it is cleared etc). - */ - public void setLowProfile(boolean lowProfile) { - if (mLowProfile != lowProfile) { - mLowProfile = lowProfile; - int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; - applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); - } - } - - /** - * Returns whether or not this dream is in low profile mode. Defaults to true. - * - * @see #setLowProfile(boolean) - * @hide - */ - public boolean isLowProfile() { - return getSystemUiVisibilityFlagValue(View.SYSTEM_UI_FLAG_LOW_PROFILE, mLowProfile); - } - - /** * Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} * on the dream's window. * @@ -1094,10 +1067,6 @@ public class DreamService extends Service implements Window.Callback { // along well. Dreams usually don't need such bars anyways, so disable them by default. mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); - mWindow.getDecorView().addOnAttachStateChangeListener( new View.OnAttachStateChangeListener() { @Override @@ -1126,18 +1095,6 @@ public class DreamService extends Service implements Window.Callback { } } - private boolean getSystemUiVisibilityFlagValue(int flag, boolean defaultValue) { - View v = mWindow == null ? null : mWindow.getDecorView(); - return v == null ? defaultValue : (v.getSystemUiVisibility() & flag) != 0; - } - - private void applySystemUiVisibilityFlags(int flags, int mask) { - View v = mWindow == null ? null : mWindow.getDecorView(); - if (v != null) { - v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask)); - } - } - private int applyFlags(int oldFlags, int flags, int mask) { return (oldFlags&~mask) | (flags&mask); } @@ -1163,7 +1120,6 @@ public class DreamService extends Service implements Window.Callback { pw.println(" window: " + mWindow); pw.print(" flags:"); if (isInteractive()) pw.print(" interactive"); - if (isLowProfile()) pw.print(" lowprofile"); if (isFullscreen()) pw.print(" fullscreen"); if (isScreenBright()) pw.print(" bright"); if (isWindowless()) pw.print(" windowless"); diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 21b83c660446..5c43f8f829b0 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -292,6 +292,18 @@ public class StatusBarNotification implements Parcelable { return this.user.getIdentifier(); } + /** + * Like {@link #getUserId()} but handles special users. + * @hide + */ + public int getNormalizedUserId() { + int userId = getUserId(); + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_SYSTEM; + } + return userId; + } + /** The package that the notification belongs to. */ public String getPackageName() { return pkg; diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index 362b94b83c3f..3b5a6d59e7e6 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -413,6 +413,10 @@ public class RecognizerIntent { * {@link #ACTION_VOICE_SEARCH_HANDS_FREE}, {@link #ACTION_WEB_SEARCH} to indicate whether to * only use an offline speech recognition engine. The default is false, meaning that either * network or offline recognition engines may be used. + * + * <p>Depending on the recognizer implementation, these values may have + * no effect.</p> + * */ public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE"; } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 1086774fc8ff..76ed37c51bfe 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -228,6 +228,7 @@ public final class SurfaceControl implements Parcelable { */ public long mNativeObject; private long mNativeHandle; + private Throwable mReleaseStack = null; // TODO: Move this to native. private final Object mSizeLock = new Object(); @@ -426,11 +427,18 @@ public final class SurfaceControl implements Parcelable { if (mNativeObject != 0) { release(); } - if (nativeObject != 0) { + if (nativeObject != 0) { mCloseGuard.open("release"); } mNativeObject = nativeObject; mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0; + if (mNativeObject == 0) { + if (Build.IS_DEBUGGABLE) { + mReleaseStack = new Throwable("assigned zero nativeObject here"); + } + } else { + mReleaseStack = null; + } } /** @@ -989,11 +997,22 @@ public final class SurfaceControl implements Parcelable { nativeRelease(mNativeObject); mNativeObject = 0; mNativeHandle = 0; + if (Build.IS_DEBUGGABLE) { + mReleaseStack = new Throwable("released here"); + } mCloseGuard.close(); } } /** + * Returns the call stack that assigned mNativeObject to zero. + * @hide + */ + public Throwable getReleaseStack() { + return mReleaseStack; + } + + /** * Disconnect any client still connected to the surface. * @hide */ @@ -1004,8 +1023,11 @@ public final class SurfaceControl implements Parcelable { } private void checkNotReleased() { - if (mNativeObject == 0) throw new NullPointerException( - "mNativeObject is null. Have you called release() already?"); + if (mNativeObject == 0) { + Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack); + throw new NullPointerException( + "mNativeObject of " + this + " is null. Have you called release() already?"); + } } /** diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 83a79344917c..6d3dbfe16b78 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1258,13 +1258,6 @@ public final class AutofillManager { } } - if (mForAugmentedAutofillOnly) { - if (sVerbose) { - Log.v(TAG, "notifyValueChanged(): not notifying system server on " - + "augmented-only mode"); - } - return; - } if (!mEnabled || !isActiveLocked()) { if (!startAutofillIfNeededLocked(view)) { if (sVerbose) { @@ -1299,10 +1292,6 @@ public final class AutofillManager { return; } synchronized (mLock) { - if (mForAugmentedAutofillOnly) { - if (sVerbose) Log.v(TAG, "notifyValueChanged(): ignoring on augmented only mode"); - return; - } if (!mEnabled || !isActiveLocked()) { if (sVerbose) { Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId diff --git a/core/java/android/view/inline/InlineContentView.java b/core/java/android/view/inline/InlineContentView.java deleted file mode 100644 index 3df201c9145d..000000000000 --- a/core/java/android/view/inline/InlineContentView.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view.inline; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.graphics.PixelFormat; -import android.util.AttributeSet; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.ViewGroup; - -/** - * This class represents a view that holds opaque content from another app that - * you can inline in your UI. - * - * <p>Since the content presented by this view is from another security domain,it is - * shown on a remote surface preventing the host application from accessing that content. - * Also the host application cannot interact with the inlined content by injecting touch - * events or clicking programmatically. - * - * <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case - * the inined UI would not be interactive. Sometimes this is desirable, e.g. animating - * transitions. - * - * <p>By default the surface backing this view is shown on top of the hosting window such - * that the inlined content is interactive. However, you can temporarily move the surface - * under the hosting window which could be useful in some cases, e.g. animating transitions. - * At this point the inlined content will not be interactive and the touch events would - * be delivered to your app. - * - * @hide - * @removed - */ -public class InlineContentView extends ViewGroup { - - /** - * Callback for observing the lifecycle of the surface control - * that manipulates the backing secure embedded UI surface. - */ - public interface SurfaceControlCallback { - /** - * Called when the backing surface is being created. - * - * @param surfaceControl The surface control to manipulate the surface. - */ - void onCreated(@NonNull SurfaceControl surfaceControl); - - /** - * Called when the backing surface is being destroyed. - * - * @param surfaceControl The surface control to manipulate the surface. - */ - void onDestroyed(@NonNull SurfaceControl surfaceControl); - } - - private final @NonNull SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() { - @Override - public void surfaceCreated(@NonNull SurfaceHolder holder) { - mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl()); - } - - @Override - public void surfaceChanged(@NonNull SurfaceHolder holder, - int format, int width, int height) { - /* do nothing */ - } - - @Override - public void surfaceDestroyed(@NonNull SurfaceHolder holder) { - mSurfaceControlCallback.onDestroyed(mSurfaceView.getSurfaceControl()); - } - }; - - private final @NonNull SurfaceView mSurfaceView; - - private @Nullable SurfaceControlCallback mSurfaceControlCallback; - - /** - * @inheritDoc - * - * @hide - */ - public InlineContentView(@NonNull Context context) { - this(context, null); - } - - /** - * @inheritDoc - * - * @hide - */ - public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) { - this(context, attrs, 0); - } - - /** - * @inheritDoc - * - * @hide - */ - public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - /** - * Gets the surface control. If the surface is not created this method - * returns {@code null}. - * - * @return The surface control. - * - * @see #setSurfaceControlCallback(SurfaceControlCallback) - */ - public @Nullable SurfaceControl getSurfaceControl() { - return mSurfaceView.getSurfaceControl(); - } - - /** - * @inheritDoc - * - * @hide - */ - public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes); - mSurfaceView.setZOrderOnTop(true); - mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT); - addView(mSurfaceView); - } - - /** - * Sets the embedded UI. - * @param surfacePackage The embedded UI. - * - * @hide - */ - public void setChildSurfacePackage( - @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) { - mSurfaceView.setChildSurfacePackage(surfacePackage); - } - - @Override - public void onLayout(boolean changed, int l, int t, int r, int b) { - mSurfaceView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); - } - - /** - * Sets a callback to observe the lifecycle of the surface control for - * managing the backing surface. - * - * @param callback The callback to set or {@code null} to clear. - */ - public void setSurfaceControlCallback(@Nullable SurfaceControlCallback callback) { - if (mSurfaceControlCallback != null) { - mSurfaceView.getHolder().removeCallback(mSurfaceCallback); - } - mSurfaceControlCallback = callback; - if (mSurfaceControlCallback != null) { - mSurfaceView.getHolder().addCallback(mSurfaceCallback); - } - } - - /** - * @return Whether the surface backing this view appears on top of its parent. - * - * @see #setZOrderedOnTop(boolean) - */ - public boolean isZOrderedOnTop() { - return mSurfaceView.isZOrderedOnTop(); - } - - /** - * Controls whether the backing surface is placed on top of this view's window. - * Normally, it is placed on top of the window, to allow interaction - * with the inlined UI. Via this method, you can place the surface below the - * window. This means that all of the contents of the window this view is in - * will be visible on top of its surface. - * - * <p> The Z ordering can be changed dynamically if the backing surface is - * created, otherwise the ordering would be applied at surface construction time. - * - * @param onTop Whether to show the surface on top of this view's window. - * - * @see #isZOrderedOnTop() - */ - public boolean setZOrderedOnTop(boolean onTop) { - return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true); - } -} diff --git a/core/java/android/view/inline/InlinePresentationSpec.aidl b/core/java/android/view/inline/InlinePresentationSpec.aidl deleted file mode 100644 index 680ee4e14b54..000000000000 --- a/core/java/android/view/inline/InlinePresentationSpec.aidl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view.inline; - -/** - * @hide - * @removed - */ -parcelable InlinePresentationSpec; diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java deleted file mode 100644 index d777cb8d8e0b..000000000000 --- a/core/java/android/view/inline/InlinePresentationSpec.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view.inline; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Bundle; -import android.os.Parcelable; -import android.util.Size; - -import com.android.internal.util.DataClass; - -import java.util.ArrayList; -import java.util.List; - -/** - * This class represents the presentation specification by which an inline suggestion - * should abide when constructing its UI. Since suggestions are inlined in a - * host application while provided by another source, they need to be consistent - * with the host's look at feel to allow building smooth and integrated UIs. - * - * @hide - * @removed - */ -@DataClass(genEqualsHashCode = true, genToString = true, genBuilder = true) -public final class InlinePresentationSpec implements Parcelable { - - /** The minimal size of the suggestion. */ - @NonNull - private final Size mMinSize; - /** The maximal size of the suggestion. */ - @NonNull - private final Size mMaxSize; - - /** - * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case - * the default system UI style will be used. - */ - @NonNull - private final Bundle mStyle; - - private static Bundle defaultStyle() { - return Bundle.EMPTY; - } - - /** @hide */ - @DataClass.Suppress({"setMaxSize", "setMinSize"}) - abstract static class BaseBuilder { - } - - /** - * @hide - */ - public android.widget.inline.InlinePresentationSpec toWidget() { - final android.widget.inline.InlinePresentationSpec.Builder builder = - new android.widget.inline.InlinePresentationSpec.Builder( - getMinSize(), getMaxSize()); - final Bundle style = getStyle(); - if (style != null) { - builder.setStyle(style); - } - return builder.build(); - } - - /** - * @hide - */ - public static android.view.inline.InlinePresentationSpec fromWidget( - android.widget.inline.InlinePresentationSpec widget) { - final android.view.inline.InlinePresentationSpec.Builder builder = - new android.view.inline.InlinePresentationSpec.Builder( - widget.getMinSize(), widget.getMaxSize()); - final Bundle style = widget.getStyle(); - if (style != null) { - builder.setStyle(style); - } - return builder.build(); - } - - /** - * @hide - */ - public static List<android.view.inline.InlinePresentationSpec> fromWidgets( - List<android.widget.inline.InlinePresentationSpec> widgets) { - final ArrayList<android.view.inline.InlinePresentationSpec> convertedSpecs = - new ArrayList<>(); - for (int i = 0; i < widgets.size(); i++) { - convertedSpecs.add(fromWidget(widgets.get(i))); - } - return convertedSpecs; - } - - - - // Code below generated by codegen v1.0.15. - // - // DO NOT MODIFY! - // CHECKSTYLE:OFF Generated code - // - // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java - // - // To exclude the generated code from IntelliJ auto-formatting enable (one-time): - // Settings > Editor > Code Style > Formatter Control - //@formatter:off - - - @DataClass.Generated.Member - /* package-private */ InlinePresentationSpec( - @NonNull Size minSize, - @NonNull Size maxSize, - @NonNull Bundle style) { - this.mMinSize = minSize; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mMinSize); - this.mMaxSize = maxSize; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mMaxSize); - this.mStyle = style; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mStyle); - - // onConstructed(); // You can define this method to get a callback - } - - /** - * The minimal size of the suggestion. - */ - @UnsupportedAppUsage - @DataClass.Generated.Member - public @NonNull Size getMinSize() { - return mMinSize; - } - - /** - * The maximal size of the suggestion. - */ - @UnsupportedAppUsage - @DataClass.Generated.Member - public @NonNull Size getMaxSize() { - return mMaxSize; - } - - /** - * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case - * the default system UI style will be used. - */ - @DataClass.Generated.Member - public @NonNull Bundle getStyle() { - return mStyle; - } - - @Override - @DataClass.Generated.Member - public String toString() { - // You can override field toString logic by defining methods like: - // String fieldNameToString() { ... } - - return "InlinePresentationSpec { " + - "minSize = " + mMinSize + ", " + - "maxSize = " + mMaxSize + ", " + - "style = " + mStyle + - " }"; - } - - @Override - @DataClass.Generated.Member - public boolean equals(@Nullable Object o) { - // You can override field equality logic by defining either of the methods like: - // boolean fieldNameEquals(InlinePresentationSpec other) { ... } - // boolean fieldNameEquals(FieldType otherValue) { ... } - - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - @SuppressWarnings("unchecked") - InlinePresentationSpec that = (InlinePresentationSpec) o; - //noinspection PointlessBooleanExpression - return true - && java.util.Objects.equals(mMinSize, that.mMinSize) - && java.util.Objects.equals(mMaxSize, that.mMaxSize) - && java.util.Objects.equals(mStyle, that.mStyle); - } - - @Override - @DataClass.Generated.Member - public int hashCode() { - // You can override field hashCode logic by defining methods like: - // int fieldNameHashCode() { ... } - - int _hash = 1; - _hash = 31 * _hash + java.util.Objects.hashCode(mMinSize); - _hash = 31 * _hash + java.util.Objects.hashCode(mMaxSize); - _hash = 31 * _hash + java.util.Objects.hashCode(mStyle); - return _hash; - } - - @Override - @DataClass.Generated.Member - public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { - // You can override field parcelling by defining methods like: - // void parcelFieldName(Parcel dest, int flags) { ... } - - dest.writeSize(mMinSize); - dest.writeSize(mMaxSize); - dest.writeBundle(mStyle); - } - - @Override - @DataClass.Generated.Member - public int describeContents() { return 0; } - - /** @hide */ - @SuppressWarnings({"unchecked", "RedundantCast"}) - @DataClass.Generated.Member - /* package-private */ InlinePresentationSpec(@NonNull android.os.Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - Size minSize = (Size) in.readSize(); - Size maxSize = (Size) in.readSize(); - Bundle style = in.readBundle(); - - this.mMinSize = minSize; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mMinSize); - this.mMaxSize = maxSize; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mMaxSize); - this.mStyle = style; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mStyle); - - // onConstructed(); // You can define this method to get a callback - } - - @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<InlinePresentationSpec> CREATOR - = new Parcelable.Creator<InlinePresentationSpec>() { - @Override - public InlinePresentationSpec[] newArray(int size) { - return new InlinePresentationSpec[size]; - } - - @Override - public InlinePresentationSpec createFromParcel(@NonNull android.os.Parcel in) { - return new InlinePresentationSpec(in); - } - }; - - /** - * A builder for {@link InlinePresentationSpec} - */ - @SuppressWarnings("WeakerAccess") - @DataClass.Generated.Member - public static final class Builder extends BaseBuilder { - - private @NonNull Size mMinSize; - private @NonNull Size mMaxSize; - private @NonNull Bundle mStyle; - - private long mBuilderFieldsSet = 0L; - - /** - * Creates a new Builder. - * - * @param minSize - * The minimal size of the suggestion. - * @param maxSize - * The maximal size of the suggestion. - */ - @UnsupportedAppUsage - public Builder( - @NonNull Size minSize, - @NonNull Size maxSize) { - mMinSize = minSize; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mMinSize); - mMaxSize = maxSize; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mMaxSize); - } - - /** - * The extras encoding the UI style information. Defaults to {@code Bundle.EMPTY} in which case - * the default system UI style will be used. - */ - @DataClass.Generated.Member - public @NonNull Builder setStyle(@NonNull Bundle value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x4; - mStyle = value; - return this; - } - - /** Builds the instance. This builder should not be touched after calling this! */ - @UnsupportedAppUsage - @NonNull - public InlinePresentationSpec build() { - checkNotUsed(); - mBuilderFieldsSet |= 0x8; // Mark builder used - - if ((mBuilderFieldsSet & 0x4) == 0) { - mStyle = defaultStyle(); - } - InlinePresentationSpec o = new InlinePresentationSpec( - mMinSize, - mMaxSize, - mStyle); - return o; - } - - private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x8) != 0) { - throw new IllegalStateException( - "This Builder should not be reused. Use a new Builder instance instead"); - } - } - } - - @DataClass.Generated( - time = 1585691139012L, - codegenVersion = "1.0.15", - sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java", - inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static android.os.Bundle defaultStyle()\npublic android.widget.inline.InlinePresentationSpec toWidget()\npublic static android.view.inline.InlinePresentationSpec fromWidget(android.widget.inline.InlinePresentationSpec)\npublic static java.util.List<android.view.inline.InlinePresentationSpec> fromWidgets(java.util.List<android.widget.inline.InlinePresentationSpec>)\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []") - @Deprecated - private void __metadata() {} - - - //@formatter:on - // End of generated code - -} diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java index 3e9ffa7787f6..1c703ecf06ca 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java +++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; -import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcelable; import android.widget.inline.InlinePresentationSpec; @@ -87,17 +86,6 @@ public final class InlineSuggestionInfo implements Parcelable { return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned); } - /** - * The presentation spec to which the inflated suggestion view abides. - * - * @hide - * @removed - */ - @UnsupportedAppUsage - public @NonNull android.view.inline.InlinePresentationSpec getPresentationSpec() { - return android.view.inline.InlinePresentationSpec.fromWidget(mInlinePresentationSpec); - } - // Code below generated by codegen v1.0.15. @@ -358,10 +346,10 @@ public final class InlineSuggestionInfo implements Parcelable { }; @DataClass.Generated( - time = 1585633580662L, + time = 1586992414034L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java", - inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inline.InlinePresentationSpec getPresentationSpec()\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") + inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index af896fca932a..d282b56aedb6 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -19,7 +19,6 @@ package android.view.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; -import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.os.IBinder; import android.os.LocaleList; @@ -93,20 +92,6 @@ public final class InlineSuggestionsRequest implements Parcelable { private int mHostDisplayId; /** - * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion - * count is larger than the number of specs in the list, then the last spec is used for the - * remainder of the suggestions. The list should not be empty. - * - * @hide - * @removed - */ - @UnsupportedAppUsage - @NonNull - public List<android.view.inline.InlinePresentationSpec> getPresentationSpecs() { - return android.view.inline.InlinePresentationSpec.fromWidgets(mInlinePresentationSpecs); - } - - /** * @hide * @see {@link #mHostInputToken}. */ @@ -170,17 +155,6 @@ public final class InlineSuggestionsRequest implements Parcelable { /** @hide */ abstract static class BaseBuilder { - /** - * @hide - * @removed - */ - @UnsupportedAppUsage - @NonNull - public Builder addPresentationSpecs( - @NonNull android.view.inline.InlinePresentationSpec value) { - return ((Builder) this).addInlinePresentationSpecs(value.toWidget()); - } - abstract Builder setInlinePresentationSpecs( @NonNull List<android.widget.inline.InlinePresentationSpec> specs); @@ -608,10 +582,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1585768018462L, + time = 1586992395497L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs()\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(android.view.inline.InlinePresentationSpec)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index d64b5f1118dc..be66d0c238cc 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -232,9 +232,8 @@ public class AccessibilityShortcutController { } /** - * Show toast if current assigned shortcut target is an accessibility service and its target - * sdk version is less than or equal to Q, or greater than Q and does not request - * accessibility button. + * Show toast to alert the user that the accessibility shortcut turned on or off an + * accessibility service. */ private void showToast() { final AccessibilityServiceInfo serviceInfo = getInfoForTargetService(); @@ -247,12 +246,15 @@ public class AccessibilityShortcutController { } final boolean requestA11yButton = (serviceInfo.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; - if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo - .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton) { + final boolean isServiceEnabled = isServiceEnabled(serviceInfo); + if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion + > Build.VERSION_CODES.Q && requestA11yButton && isServiceEnabled) { + // An accessibility button callback is sent to the target accessibility service. + // No need to show up a toast in this case. return; } // For accessibility services, show a toast explaining what we're doing. - String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo) + String toastMessageFormatString = mContext.getString(isServiceEnabled ? R.string.accessibility_shortcut_disabling_service : R.string.accessibility_shortcut_enabling_service); String toastMessage = String.format(toastMessageFormatString, serviceName); diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java index eda04a6a322a..ea41618a982f 100644 --- a/core/java/com/android/internal/app/procstats/DumpUtils.java +++ b/core/java/com/android/internal/app/procstats/DumpUtils.java @@ -16,14 +16,39 @@ package com.android.internal.app.procstats; +import static com.android.internal.app.procstats.ProcessStats.ADJ_COUNT; +import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_COUNT; +import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL; +import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW; +import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE; +import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL; +import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING; +import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_MOD; +import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_OFF; +import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_ON; +import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY; +import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT; +import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT; +import static com.android.internal.app.procstats.ProcessStats.STATE_HOME; +import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND; +import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND; +import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY; +import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING; +import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT; +import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER; +import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE; +import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING; +import static com.android.internal.app.procstats.ProcessStats.STATE_TOP; + import android.os.UserHandle; import android.service.procstats.ProcessStatsEnums; import android.service.procstats.ProcessStatsStateProto; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; -import static com.android.internal.app.procstats.ProcessStats.*; - import java.io.PrintWriter; import java.util.ArrayList; @@ -38,6 +63,7 @@ public final class DumpUtils { public static final String[] STATE_NAMES_CSV; static final String[] STATE_TAGS; static final int[] STATE_PROTO_ENUMS; + private static final int[] PROCESS_STATS_STATE_TO_AGGREGATED_STATE; // Make the mapping easy to update. static { @@ -126,6 +152,39 @@ public final class DumpUtils { STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT; STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY; + + // Remap states, as defined by ProcessStats.java, to a reduced subset of states for data + // aggregation / size reduction purposes. + PROCESS_STATS_STATE_TO_AGGREGATED_STATE = new int[STATE_COUNT]; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_PERSISTENT] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_PERSISTENT; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_TOP] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_TOP; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_FOREGROUND] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_BACKGROUND] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BACKUP] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND; + // "Restarting" is not a real state, so this shouldn't exist. + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE_RESTARTING] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_UNKNOWN; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_RECEIVER] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_RECEIVER; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HEAVY_WEIGHT] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HOME] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_LAST_ACTIVITY] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_EMPTY] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; } public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { @@ -455,4 +514,29 @@ public final class DumpUtils { } return itemName; } + + /** + * Aggregate process states to reduce size of statistics logs. + * + * <p>Involves unpacking the three parts of state (process state / device memory state / + * screen state), manipulating the elements, then re-packing the new values into a single + * int. This integer is guaranteed to be unique for any given combination of state elements. + * + * @param curState current state as used in mCurState in {@class ProcessState} ie. a value + * combined from the process's state, the device's memory pressure state, and + * the device's screen on/off state. + * @return an integer representing the combination of screen state and process state, where + * process state has been aggregated. + */ + public static int aggregateCurrentProcessState(int curState) { + int screenStateIndex = curState / (ADJ_SCREEN_MOD * STATE_COUNT); + // extract process state from the compound state variable (discarding memory state) + int procStateIndex = curState % STATE_COUNT; + + // Remap process state per array above. + procStateIndex = PROCESS_STATS_STATE_TO_AGGREGATED_STATE[procStateIndex]; + + // Pack screen & process state using bit shifting + return (procStateIndex << 8) | screenStateIndex; + } } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 60892557891b..55ea3159c44c 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -250,7 +250,7 @@ message TaskProto { reserved 3; // activity optional bool fills_parent = 4; optional .android.graphics.RectProto bounds = 5; - optional .android.graphics.RectProto displayed_bounds = 6; + optional .android.graphics.RectProto displayed_bounds = 6 [deprecated=true]; optional bool defer_removal = 7; optional int32 surface_width = 8; optional int32 surface_height = 9; diff --git a/core/proto/android/service/procstats_enum.proto b/core/proto/android/service/procstats_enum.proto index cc3fe5af775b..2abf3730aa9f 100644 --- a/core/proto/android/service/procstats_enum.proto +++ b/core/proto/android/service/procstats_enum.proto @@ -76,3 +76,27 @@ enum ServiceOperationState { SERVICE_OPERATION_STATE_BOUND = 4; SERVICE_OPERATION_STATE_EXECUTING = 5; } + +// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java +// and not frameworks/base/core/java/android/app/ActivityManager.java +enum AggregatedProcessState { + AGGREGATED_PROCESS_STATE_UNKNOWN = 0; + // Persistent system process; PERSISTENT or PERSISTENT_UI in ActivityManager + AGGREGATED_PROCESS_STATE_PERSISTENT = 1; + // Top activity; actually any visible activity; TOP or TOP_SLEEPING in ActivityManager + AGGREGATED_PROCESS_STATE_TOP = 2; + // Bound top foreground process; BOUND_TOP or BOUND_FOREGROUND_SERVICE in ActivityManager + AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS = 3; + // Important foreground process; FOREGROUND_SERVICE in ActivityManager + AGGREGATED_PROCESS_STATE_FGS = 4; + // Important foreground process ; IMPORTANT_FOREGROUND in ActivityManager + AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND = 5; + // Various background processes; IMPORTANT_BACKGROUND, TRANSIENT_BACKGROUND, BACKUP, SERVICE, + // HEAVY_WEIGHT in ActivityManager + AGGREGATED_PROCESS_STATE_BACKGROUND = 6; + // Process running a receiver; RECEIVER in ActivityManager + AGGREGATED_PROCESS_STATE_RECEIVER = 7; + // Various cached processes; HOME, LAST_ACTIVITY, CACHED_ACTIVITY, CACHED_RECENT, + // CACHED_ACTIVITY_CLIENT, CACHED_EMPTY in ActivityManager + AGGREGATED_PROCESS_STATE_CACHED = 8; +}
\ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index 4a33da680585..b21504c73772 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -462,6 +462,7 @@ public class AccessibilityShortcutControllerTest { configureValidShortcutService(); configureApplicationTargetSdkVersion(Build.VERSION_CODES.R); configureRequestAccessibilityButton(); + configureEnabledService(); Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); getController().performAccessibilityShortcut(); @@ -610,6 +611,11 @@ public class AccessibilityShortcutControllerTest { }).when(mHandler).sendMessageAtTime(any(), anyLong()); } + private void configureEnabledService() throws Exception { + when(mAccessibilityManagerService.getEnabledAccessibilityServiceList(anyInt(), anyInt())) + .thenReturn(Collections.singletonList(mServiceInfo)); + } + private AccessibilityShortcutController getController() { AccessibilityShortcutController accessibilityShortcutController = new AccessibilityShortcutController(mContext, mHandler, 0); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java index c11e1a03cb00..6fbee16e3dae 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java @@ -30,13 +30,17 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; +import android.content.pm.UserInfo; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; +import android.os.UserManager; import android.permission.IPermissionManager; import android.util.Log; +import java.util.List; + /** * Select which activity is the first visible activity of the installation and forward the intent to * it. @@ -47,6 +51,7 @@ public class InstallStart extends Activity { private static final String DOWNLOADS_AUTHORITY = "downloads"; private IPackageManager mIPackageManager; private IPermissionManager mIPermissionManager; + private UserManager mUserManager; private boolean mAbortInstall = false; @Override @@ -54,6 +59,7 @@ public class InstallStart extends Activity { super.onCreate(savedInstanceState); mIPackageManager = AppGlobals.getPackageManager(); mIPermissionManager = AppGlobals.getPermissionManager(); + mUserManager = getSystemService(UserManager.class); Intent intent = getIntent(); String callingPackage = getCallingPackage(); @@ -144,13 +150,16 @@ public class InstallStart extends Activity { if (packages == null) { return false; } + final List<UserInfo> users = mUserManager.getUsers(); for (String packageName : packages) { - try { - if (uid == getPackageManager().getPackageUid(packageName, 0)) { - return true; + for (UserInfo user : users) { + try { + if (uid == getPackageManager().getPackageUidAsUser(packageName, user.id)) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // Ignore and try the next package } - } catch (PackageManager.NameNotFoundException e) { - // Ignore and try the next package } } } catch (RemoteException rexc) { diff --git a/packages/SystemUI/res/drawable/control_background.xml b/packages/SystemUI/res/drawable/control_background.xml index 29b4efa48fa1..cf298b70f3db 100644 --- a/packages/SystemUI/res/drawable/control_background.xml +++ b/packages/SystemUI/res/drawable/control_background.xml @@ -17,7 +17,8 @@ */ --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item> + <item + android:id="@+id/background"> <shape> <solid android:color="@color/control_default_background" /> <corners android:radius="@dimen/control_corner_radius" /> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index e7ef8ccf4eba..622e4ccef487 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1252,6 +1252,7 @@ <dimen name="control_status_expanded">18sp</dimen> <dimen name="control_base_item_margin">2dp</dimen> <dimen name="control_status_padding">3dp</dimen> + <fraction name="controls_toggle_bg_intensity">5%</fraction> <!-- Home Controls activity view detail panel--> <dimen name="controls_activity_view_top_padding">25dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d2654d6d7d9a..49420e8620d1 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1829,15 +1829,15 @@ <!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary --> <string name="notification_channel_summary_default">Gets your attention with sound or vibration.</string> + <!-- [CHAR LIMIT=150] Conversation Notification Importance title: normal conversation level, with bubbling summary --> + <string name="notification_channel_summary_default_with_bubbles">Gets your attention with sound or vibration. Conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default.</string> + <!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary --> <string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string> <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary --> <string name="notification_channel_summary_priority">Shows at top of conversation section and appears as a bubble.</string> - <!--[CHAR LIMIT=150] Conversation inline controls footer shown when all conversations from the app are allowed to show as bubbles --> - <string name="notification_conversation_channel_all_bubble">All conversations from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubble by default. Manage in <xliff:g id="app_name" example="Settings">%2$s</xliff:g>.</string> - <!--[CHAR LIMIT=30] Linkable text to Settings app --> <string name="notification_conversation_channel_settings">Settings</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 118aa5b3f96a..7e24f5dbbd50 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -564,7 +564,7 @@ <style name="TextAppearance.NotificationImportanceButton"> <item name="android:textSize">@dimen/notification_importance_button_text</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> - <item name="android:textColor">?android:attr/colorAccent</item> + <item name="android:textColor">@color/notification_guts_priority_contents</item> <item name="android:gravity">center</item> </style> diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 922fb69b3fdc..7861211e802d 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -948,7 +948,12 @@ public class ScreenDecorations extends SystemUI implements Tunable { int dw = flipped ? lh : lw; int dh = flipped ? lw : lh; - mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), dw, dh)); + Path path = DisplayCutout.pathFromResources(getResources(), dw, dh); + if (path != null) { + mBoundingPath.set(path); + } else { + mBoundingPath.reset(); + } Matrix m = new Matrix(); transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m); mBoundingPath.transform(m); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java index d8a11d36a335..e6a62c26712a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java @@ -17,6 +17,7 @@ package com.android.systemui.biometrics; import android.content.Context; +import android.os.UserHandle; import android.text.InputType; import android.util.AttributeSet; import android.view.KeyEvent; @@ -68,6 +69,7 @@ public class AuthCredentialPasswordView extends AuthCredentialView protected void onAttachedToWindow() { super.onAttachedToWindow(); + mPasswordField.setTextOperationUser(UserHandle.of(mUserId)); if (mCredentialType == Utils.CREDENTIAL_PIN) { mPasswordField.setInputType( InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index 8bf259182544..496e60ddf99e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -286,6 +286,7 @@ public abstract class AuthCredentialView extends LinearLayout { if (matched) { mClearErrorRunnable.run(); + mLockPatternUtils.userPresent(mEffectiveUserId); mCallback.onCredentialMatched(attestation); } else { if (timeoutMs > 0) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index da5c2968c6ac..c8e9a687ad23 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -108,7 +108,6 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; /** @@ -162,14 +161,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // 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; - // If we're auto-bubbling bubbles via a whitelist, we need to track which notifs from that app - // have been "demoted" back to a notification so that we don't auto-bubbles those again. - // Doesn't persist across reboots, not a long-term solution. - private final HashSet<String> mUserBlockedBubbles; - // Bubbles get added to the status bar view private final NotificationShadeWindowController mNotificationShadeWindowController; private final ZenModeController mZenModeController; @@ -412,9 +403,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } }); - mUserCreatedBubbles = new HashSet<>(); - mUserBlockedBubbles = new HashSet<>(); - mBubbleIconFactory = new BubbleIconFactory(context); } @@ -474,8 +462,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi (entry != null && entry.isRowDismissed() && !isAppCancel) || isClearAll || isUserDimiss || isSummaryCancel; - if (userRemovedNotif || isUserCreatedBubble(key) - || isSummaryOfUserCreatedBubble(entry)) { + if (userRemovedNotif) { return handleDismissalInterception(entry); } @@ -860,27 +847,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } /** - * Whether this bubble was explicitly created by the user via a SysUI affordance. - */ - boolean isUserCreatedBubble(String key) { - return mUserCreatedBubbles.contains(key); - } - - boolean isSummaryOfUserCreatedBubble(NotificationEntry entry) { - if (isSummaryOfBubbles(entry)) { - List<Bubble> bubbleChildren = - mBubbleData.getBubblesInGroup(entry.getSbn().getGroupKey()); - for (int i = 0; i < bubbleChildren.size(); i++) { - // Check if any are user-created (i.e. experimental bubbles) - if (isUserCreatedBubble(bubbleChildren.get(i).getKey())) { - return true; - } - } - } - return false; - } - - /** * Removes the bubble with the given NotificationEntry. * <p> * Must be called from the main thread. @@ -893,37 +859,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } private void onEntryAdded(NotificationEntry entry) { - boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey()); - boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey()); - boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments( - mContext, entry, previouslyUserCreated, userBlocked); - if (mNotificationInterruptStateProvider.shouldBubbleUp(entry) - && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) { - if (wasAdjusted && !previouslyUserCreated) { - // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated - mUserCreatedBubbles.add(entry.getKey()); - } + && canLaunchInActivityView(mContext, entry)) { updateBubble(entry); } } private void onEntryUpdated(NotificationEntry entry) { - boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey()); - boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey()); - boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments( - mContext, entry, previouslyUserCreated, userBlocked); - boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry) - && (canLaunchInActivityView(mContext, entry) || wasAdjusted); + && canLaunchInActivityView(mContext, entry); if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) { // It was previously a bubble but no longer a bubble -- lets remove it removeBubble(entry, DISMISS_NO_LONGER_BUBBLE); } else if (shouldBubble) { - if (wasAdjusted && !previouslyUserCreated) { - // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated - mUserCreatedBubbles.add(entry.getKey()); - } updateBubble(entry); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 93fb6972fad5..3524696dbc79 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -193,7 +193,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList + " mActivityViewStatus=" + mActivityViewStatus + " bubble=" + getBubbleKey()); } - if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) { + if (mBubble != null) { // Must post because this is called from a binder thread. post(() -> mBubbleController.removeBubble(mBubble.getEntry(), BubbleController.DISMISS_TASK_FINISHED)); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java index 41dbb489c2f6..2060391d38a3 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java @@ -57,16 +57,13 @@ import java.util.List; public class BubbleExperimentConfig { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES; - private static final String SHORTCUT_DUMMY_INTENT = "bubble_experiment_shortcut_intent"; - private static PendingIntent sDummyShortcutIntent; - private static final int BUBBLE_HEIGHT = 10000; private static final String ALLOW_ANY_NOTIF_TO_BUBBLE = "allow_any_notif_to_bubble"; private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false; private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble"; - private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = true; + private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false; private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble"; private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 1cabe221bef1..0534efc0949d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -374,8 +374,9 @@ public class BubbleStackView extends FrameLayout { @Override public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { mExpandedAnimationController.dismissDraggedOutBubble( - mExpandedAnimationController.getDraggedOutBubble(), - BubbleStackView.this::dismissMagnetizedObject); + mExpandedAnimationController.getDraggedOutBubble() /* bubble */, + mDismissTargetContainer.getHeight() /* translationYBy */, + BubbleStackView.this::dismissMagnetizedObject /* after */); hideDismissTarget(); } }; @@ -405,7 +406,8 @@ public class BubbleStackView extends FrameLayout { @Override public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { - mStackAnimationController.implodeStack( + mStackAnimationController.animateStackDismissal( + mDismissTargetContainer.getHeight() /* translationYBy */, () -> { resetDesaturationAndDarken(); dismissMagnetizedObject(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java index 501e5024d940..c96f9a470ca4 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java @@ -139,22 +139,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask StatusBarNotification sbn = b.getEntry().getSbn(); String packageName = sbn.getPackageName(); - // Real shortcut info for this bubble String bubbleShortcutId = b.getEntry().getBubbleMetadata().getShortcutId(); if (bubbleShortcutId != null) { - info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c, packageName, - sbn.getUser(), bubbleShortcutId); - } else { - // Check for experimental shortcut - String shortcutId = sbn.getNotification().getShortcutId(); - if (BubbleExperimentConfig.useShortcutInfoToBubble(c) && shortcutId != null) { - info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c, - packageName, - sbn.getUser(), shortcutId); - } + info.shortcutInfo = b.getEntry().getRanking().getShortcutInfo(); } - // App name & app icon PackageManager pm = c.getPackageManager(); ApplicationInfo appInfo; 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 d974adc34ee0..0002e862bb41 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -329,7 +329,7 @@ public class ExpandedAnimationController } /** Plays a dismiss animation on the dragged out bubble. */ - public void dismissDraggedOutBubble(View bubble, Runnable after) { + public void dismissDraggedOutBubble(View bubble, float translationYBy, Runnable after) { if (bubble == null) { return; } @@ -337,6 +337,7 @@ public class ExpandedAnimationController .withStiffness(SpringForce.STIFFNESS_HIGH) .scaleX(1.1f) .scaleY(1.1f) + .translationY(bubble.getTranslationY() + translationYBy) .alpha(0f, after) .start(); 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 00de8b4a51b8..5f3a2bd9eb8b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -647,17 +647,18 @@ public class StackAnimationController extends } /** - * 'Implode' the stack by shrinking the bubbles via chained animations and fading them out. + * 'Implode' the stack by shrinking the bubbles, fading them out, and translating them down. */ - public void implodeStack(Runnable after) { - // Pop and fade the bubbles sequentially. - animationForChildAtIndex(0) - .scaleX(0.5f) - .scaleY(0.5f) - .alpha(0f) - .withDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY) - .withStiffness(SpringForce.STIFFNESS_HIGH) - .start(after); + public void animateStackDismissal(float translationYBy, Runnable after) { + animationsForChildrenFromIndex(0, (index, animation) -> + animation + .scaleX(0.5f) + .scaleY(0.5f) + .alpha(0f) + .translationY( + mLayout.getChildAt(index).getTranslationY() + translationYBy) + .withStiffness(SpringForce.STIFFNESS_HIGH)) + .startAll(after); } /** @@ -710,8 +711,6 @@ public class StackAnimationController extends if (property.equals(DynamicAnimation.TRANSLATION_X) || property.equals(DynamicAnimation.TRANSLATION_Y)) { return index + 1; - } else if (isStackStuckToTarget()) { - return index + 1; // Chain all animations in dismiss (scale, alpha, etc. are used). } else { return NONE; } 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 055adc6f6774..93e1bd444938 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -66,7 +66,10 @@ class ControlViewHolder( ) } + private val toggleBackgroundIntensity: Float = layout.context.resources + .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1) private var stateAnimator: ValueAnimator? = null + private val baseLayer: GradientDrawable val icon: ImageView = layout.requireViewById(R.id.icon) val status: TextView = layout.requireViewById(R.id.status) val title: TextView = layout.requireViewById(R.id.title) @@ -85,6 +88,7 @@ class ControlViewHolder( ld.mutate() clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable clipLayer.alpha = ALPHA_DISABLED + baseLayer = ld.findDrawableByLayerId(R.id.background) as GradientDrawable // needed for marquee to start status.setSelected(true) } @@ -171,11 +175,12 @@ class ControlViewHolder( val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset) - val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme()) - val (bg, newAlpha) = if (enabled) { - Pair(ri.enabledBackground, ALPHA_ENABLED) + val fg = context.resources.getColorStateList(ri.foreground, context.theme) + val bg = context.resources.getColor(R.color.control_default_background, context.theme) + val (clip, newAlpha) = if (enabled) { + listOf(ri.enabledBackground, ALPHA_ENABLED) } else { - Pair(R.color.control_default_background, ALPHA_DISABLED) + listOf(R.color.control_default_background, ALPHA_DISABLED) } status.setTextColor(fg) @@ -187,14 +192,22 @@ class ControlViewHolder( } (clipLayer.getDrawable() as GradientDrawable).apply { - val newColor = context.resources.getColor(bg, context.theme) + val newClipColor = context.resources.getColor(clip, context.theme) + val newBaseColor = if (behavior is ToggleRangeBehavior) { + ColorUtils.blendARGB(bg, newClipColor, toggleBackgroundIntensity) + } else { + bg + } stateAnimator?.cancel() if (animated) { - val oldColor = color?.defaultColor ?: newColor + val oldColor = color?.defaultColor ?: newClipColor + val oldBaseColor = baseLayer.color?.defaultColor ?: newBaseColor stateAnimator = ValueAnimator.ofInt(clipLayer.alpha, newAlpha).apply { addUpdateListener { alpha = it.animatedValue as Int - setColor(ColorUtils.blendARGB(oldColor, newColor, it.animatedFraction)) + setColor(ColorUtils.blendARGB(oldColor, newClipColor, it.animatedFraction)) + baseLayer.setColor(ColorUtils.blendARGB(oldBaseColor, + newBaseColor, it.animatedFraction)) } addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { @@ -207,7 +220,8 @@ class ControlViewHolder( } } else { alpha = newAlpha - setColor(newColor) + setColor(newClipColor) + baseLayer.setColor(newBaseColor) } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 62efd8ce4cee..8492fef97df5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -82,6 +82,7 @@ public class MediaControlPanel { protected ComponentName mRecvComponent; private MediaDevice mDevice; private boolean mIsRegistered = false; + private String mKey; private final int[] mActionIds; @@ -203,14 +204,15 @@ public class MediaControlPanel { * @param bgColor * @param contentIntent * @param appNameString - * @param device + * @param key */ public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, - int bgColor, PendingIntent contentIntent, String appNameString) { + int bgColor, PendingIntent contentIntent, String appNameString, String key) { mToken = token; mForegroundColor = iconColor; mBackgroundColor = bgColor; mController = new MediaController(mContext, mToken); + mKey = key; MediaMetadata mediaMetadata = mController.getMetadata(); @@ -326,6 +328,14 @@ public class MediaControlPanel { } /** + * Return the original notification's key + * @return The notification key + */ + public String getKey() { + return mKey; + } + + /** * Check whether this player has an attached media session. * @return whether there is a controller with a current media session. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt index b7658a9f178d..51c157a56560 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt @@ -61,6 +61,7 @@ class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> { if (!data.enabled) { seekBarView.setEnabled(false) seekBarView.getThumb().setAlpha(0) + seekBarView.setProgress(0) elapsedTimeView.setText("") totalTimeView.setText("") return diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt index dd83e42cde2d..142510030a5f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt @@ -77,13 +77,25 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) { val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L val position = playbackState?.position?.toInt() val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() - val enabled = if (duration != null && duration <= 0) false else true + val enabled = if (playbackState == null || + playbackState?.getState() == PlaybackState.STATE_NONE || + (duration != null && duration <= 0)) false else true _data = Progress(enabled, seekAvailable, position, duration, color) if (shouldPollPlaybackPosition()) { checkPlaybackPosition() } } + /** + * Puts the seek bar into a resumption state. + * + * This should be called when the media session behind the controller has been destroyed. + */ + @AnyThread + fun clearController() = bgExecutor.execute { + _data = _data.copy(enabled = false) + } + @AnyThread private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({ val currentPosition = controller?.playbackState?.position?.toInt() 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 c3779efcf4b2..ba9a30fb554f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -339,18 +339,18 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Override public void onPipTransitionFinished(ComponentName activity, int direction) { - onPipTransitionFinishedOrCanceled(); + onPipTransitionFinishedOrCanceled(direction); } @Override public void onPipTransitionCanceled(ComponentName activity, int direction) { - onPipTransitionFinishedOrCanceled(); + onPipTransitionFinishedOrCanceled(direction); } - private void onPipTransitionFinishedOrCanceled() { + private void onPipTransitionFinishedOrCanceled(int direction) { // Re-enable touches after the animation completes mTouchHandler.setTouchEnabled(true); - mTouchHandler.onPinnedStackAnimationEnded(); + mTouchHandler.onPinnedStackAnimationEnded(direction); mMenuController.onPinnedStackAnimationEnded(); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 2b9b1716cb18..ec15dd16f46e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -54,6 +54,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; @@ -129,9 +130,7 @@ public class PipMenuActivity extends Activity { } }; - private Handler mHandler = new Handler(); - private Messenger mToControllerMessenger; - private Messenger mMessenger = new Messenger(new Handler() { + private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -174,7 +173,9 @@ public class PipMenuActivity extends Activity { } } } - }); + }; + private Messenger mToControllerMessenger; + private Messenger mMessenger = new Messenger(mHandler); private final Runnable mFinishRunnable = new Runnable() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index d660b670446b..61ed40d5d782 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -30,6 +30,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.Debug; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; @@ -122,7 +123,7 @@ public class PipMenuActivityController { private boolean mStartActivityRequested; private long mStartActivityRequestedTime; private Messenger mToActivityMessenger; - private Handler mHandler = new Handler() { + private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -133,15 +134,15 @@ public class PipMenuActivityController { break; } case MESSAGE_EXPAND_PIP: { - mListeners.forEach(l -> l.onPipExpand()); + mListeners.forEach(Listener::onPipExpand); break; } case MESSAGE_DISMISS_PIP: { - mListeners.forEach(l -> l.onPipDismiss()); + mListeners.forEach(Listener::onPipDismiss); break; } case MESSAGE_SHOW_MENU: { - mListeners.forEach(l -> l.onPipShowMenu()); + mListeners.forEach(Listener::onPipShowMenu); break; } case MESSAGE_UPDATE_ACTIVITY_CALLBACK: { @@ -259,6 +260,8 @@ public class PipMenuActivityController { if (DEBUG) { Log.d(TAG, "showMenu() state=" + menuState + " hasActivity=" + (mToActivityMessenger != null) + + " allowMenuTimeout=" + allowMenuTimeout + + " willResizeMenu=" + willResizeMenu + " callers=\n" + Debug.getCallers(5, " ")); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index 0b076559ae36..d80f18a983ee 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -56,7 +56,6 @@ public class PipResizeGestureHandler { private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); private final PipBoundsHandler mPipBoundsHandler; - private final PipTouchHandler mPipTouchHandler; private final PipMotionHelper mMotionHelper; private final int mDisplayId; private final Executor mMainExecutor; @@ -70,10 +69,10 @@ public class PipResizeGestureHandler { private final Rect mTmpBounds = new Rect(); private final int mDelta; - private boolean mAllowGesture = false; + private boolean mAllowGesture; private boolean mIsAttached; private boolean mIsEnabled; - private boolean mEnablePipResize; + private boolean mEnableUserResize; private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; @@ -82,21 +81,20 @@ public class PipResizeGestureHandler { private int mCtrlType; public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler, - PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper, - DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer) { + PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig, + PipTaskOrganizer pipTaskOrganizer) { final Resources res = context.getResources(); context.getDisplay().getMetrics(mDisplayMetrics); mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); mPipBoundsHandler = pipBoundsHandler; - mPipTouchHandler = pipTouchHandler; mMotionHelper = motionHelper; mPipTaskOrganizer = pipTaskOrganizer; context.getDisplay().getRealSize(mMaxSize); mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size); - mEnablePipResize = DeviceConfig.getBoolean( + mEnableUserResize = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, PIP_USER_RESIZE, /* defaultValue = */ true); @@ -105,7 +103,7 @@ public class PipResizeGestureHandler { @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { if (properties.getKeyset().contains(PIP_USER_RESIZE)) { - mEnablePipResize = properties.getBoolean( + mEnableUserResize = properties.getBoolean( PIP_USER_RESIZE, /* defaultValue = */ true); } } @@ -134,7 +132,7 @@ public class PipResizeGestureHandler { } private void updateIsEnabled() { - boolean isEnabled = mIsAttached && mEnablePipResize; + boolean isEnabled = mIsAttached && mEnableUserResize; if (isEnabled == mIsEnabled) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 350ce293a13f..f5c83c1fffd7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -16,6 +16,7 @@ package com.android.systemui.pip.phone; +import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE; @@ -56,6 +57,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.systemui.R; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; @@ -229,7 +231,7 @@ public class PipTouchHandler { mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer, mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator); mPipResizeGestureHandler = - new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper, + new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, deviceConfig, pipTaskOrganizer); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), @@ -266,6 +268,10 @@ public class PipTouchHandler { mMagnetizedPip = mMotionHelper.getMagnetizedPip(); mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0); + + // Set the magnetic field radius equal to twice the size of the target. + mMagneticTarget.setMagneticFieldRadiusPx(targetSize * 2); + mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener); mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() { @Override @@ -339,11 +345,16 @@ public class PipTouchHandler { mPipResizeGestureHandler.onActivityUnpinned(); } - public void onPinnedStackAnimationEnded() { + public void onPinnedStackAnimationEnded( + @PipAnimationController.TransitionDirection int direction) { // Always synchronize the motion helper bounds once PiP animations finish mMotionHelper.synchronizePinnedStackBounds(); updateMovementBounds(); - mResizedBounds.set(mMotionHelper.getBounds()); + if (direction == TRANSITION_DIRECTION_TO_PIP) { + // updates mResizedBounds only if it's an entering PiP animation + // mResized should be otherwise updated in setMenuState. + mResizedBounds.set(mMotionHelper.getBounds()); + } if (mShowPipMenuOnAnimationEnd) { mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(), @@ -504,9 +515,6 @@ public class PipTouchHandler { mTargetView.setTranslationY(mTargetViewContainer.getHeight()); mTargetViewContainer.setVisibility(View.VISIBLE); - // Set the magnetic field radius to half of PIP's width. - mMagneticTarget.setMagneticFieldRadiusPx(mMotionHelper.getBounds().width()); - // Cancel in case we were in the middle of animating it out. mMagneticTargetAnimator.cancel(); mMagneticTargetAnimator diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java index e636707a9722..e4bcb0921284 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java @@ -99,15 +99,14 @@ public class QSMediaPlayer extends MediaControlPanel { * @param bgColor background color * @param actionsContainer a LinearLayout containing the media action buttons * @param notif reference to original notification - * @param device current playback device + * @param key original notification's key */ public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, - int bgColor, View actionsContainer, Notification notif) { + int bgColor, View actionsContainer, Notification notif, String key) { String appName = Notification.Builder.recoverBuilder(getContext(), notif) .loadHeaderAppName(); - super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent, - appName); + super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent, appName, key); // Media controls LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; @@ -171,6 +170,8 @@ public class QSMediaPlayer extends MediaControlPanel { public void clearControls() { super.clearControls(); + mSeekBarViewModel.clearController(); + View guts = mMediaNotifView.findViewById(R.id.media_guts); View options = mMediaNotifView.findViewById(R.id.qs_media_controls_options); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 0566b2e621db..40c8aadfd5d4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -208,9 +208,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne * @param bgColor * @param actionsContainer * @param notif + * @param key */ public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, - View actionsContainer, StatusBarNotification notif) { + View actionsContainer, StatusBarNotification notif, String key) { if (!useQsMediaPlayer(mContext)) { // Shouldn't happen, but just in case Log.e(TAG, "Tried to add media session without player!"); @@ -225,13 +226,12 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne String packageName = notif.getPackageName(); for (QSMediaPlayer p : mMediaPlayers) { if (p.getMediaSessionToken().equals(token)) { - Log.d(TAG, "a player for this session already exists"); + Log.d(TAG, "Found matching player by token " + packageName); player = p; break; - } - - if (packageName.equals(p.getMediaPlayerPackage())) { - Log.d(TAG, "found an old session for this app"); + } else if (packageName.equals(p.getMediaPlayerPackage()) && key.equals(p.getKey())) { + // Also match if it's the same package and notification key + Log.d(TAG, "Found matching player by package " + packageName + ", " + key); player = p; break; } @@ -267,7 +267,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne Log.d(TAG, "setting player session"); player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer, - notif.getNotification()); + notif.getNotification(), key); if (mMediaPlayers.size() > 0) { ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java index 0ba4cb159024..794677912626 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java @@ -67,9 +67,10 @@ public class QuickQSMediaPlayer extends MediaControlPanel { * @param actionsToShow indices of which actions to display in the mini player * (max 3: Notification.MediaStyle.MAX_MEDIA_BUTTONS_IN_COMPACT) * @param contentIntent Intent to send when user taps on the view + * @param key original notification's key */ public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, - View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) { + View actionsContainer, int[] actionsToShow, PendingIntent contentIntent, String key) { // Only update if this is a different session and currently playing String oldPackage = ""; if (getController() != null) { @@ -84,7 +85,7 @@ public class QuickQSMediaPlayer extends MediaControlPanel { return; } - super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null); + super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null, key); LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; int i = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 0d7715958995..25f1a974bc36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -250,7 +250,8 @@ class NotificationShadeDepthController @Inject constructor( private fun updateShadeBlur() { var newBlur = 0 val state = statusBarStateController.state - if (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) { + if ((state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) && + !keyguardStateController.isKeyguardFadingAway) { newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion) } shadeSpring.animateTo(newBlur) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index a27199370b16..ab2cffa57c3e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.row; import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; @@ -33,6 +34,7 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -51,6 +53,7 @@ import android.transition.TransitionManager; import android.transition.TransitionSet; import android.util.AttributeSet; import android.util.Log; +import android.util.Slog; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; @@ -91,6 +94,7 @@ public class NotificationConversationInfo extends LinearLayout implements private String mConversationId; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; + private int mAppBubble; private TextView mPriorityDescriptionView; private TextView mDefaultDescriptionView; @@ -206,6 +210,13 @@ public class NotificationConversationInfo extends LinearLayout implements mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded( getContext(), mINotificationManager, entry, mNotificationChannel); + try { + mAppBubble = mINotificationManager.getBubblePreferenceForPackage(mPackageName, mAppUid); + } catch (RemoteException e) { + Slog.e(TAG, "can't reach OS", e); + mAppBubble = BUBBLE_PREFERENCE_SELECTED; + } + bindHeader(); bindActions(); @@ -227,6 +238,11 @@ public class NotificationConversationInfo extends LinearLayout implements snooze.setOnClickListener(mOnSnoozeClick); */ + if (mAppBubble == BUBBLE_PREFERENCE_ALL) { + ((TextView) findViewById(R.id.default_summary)).setText(getResources().getString( + R.string.notification_channel_summary_default_with_bubbles, mAppName)); + } + findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick); findViewById(R.id.default_behavior).setOnClickListener(mOnDefaultClick); findViewById(R.id.silence).setOnClickListener(mOnMuteClick); @@ -264,7 +280,6 @@ public class NotificationConversationInfo extends LinearLayout implements // bindName(); bindPackage(); bindIcon(mNotificationChannel.isImportantConversation()); - } private void bindIcon(boolean important) { @@ -560,10 +575,7 @@ public class NotificationConversationInfo extends LinearLayout implements !mChannelToUpdate.isImportantConversation()); if (mChannelToUpdate.isImportantConversation()) { mChannelToUpdate.setAllowBubbles(true); - int currentPref = - mINotificationManager.getBubblePreferenceForPackage( - mAppPkg, mAppUid); - if (currentPref == BUBBLE_PREFERENCE_NONE) { + if (mAppBubble == BUBBLE_PREFERENCE_NONE) { mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid, BUBBLE_PREFERENCE_SELECTED); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 874d81db0bd2..2da2724aacb2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -193,7 +193,8 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi mBackgroundColor, mActions, compactActions, - notif.contentIntent); + notif.contentIntent, + sbn.getKey()); QSPanel bigPanel = ctrl.getNotificationShadeView().findViewById( com.android.systemui.R.id.quick_settings_panel); bigPanel.addMediaSession(token, @@ -201,7 +202,8 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi tintColor, mBackgroundColor, mActions, - sbn); + sbn, + sbn.getKey()); } boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar(); diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt index f27bdbfbeda0..e905e6772074 100644 --- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt +++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt @@ -27,6 +27,7 @@ import android.provider.Settings import android.view.MotionEvent import android.view.VelocityTracker import android.view.View +import android.view.ViewConfiguration import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringForce @@ -146,6 +147,10 @@ abstract class MagnetizedObject<T : Any>( private val velocityTracker: VelocityTracker = VelocityTracker.obtain() private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + private var touchDown = PointF() + private var touchSlop = 0 + private var movedBeyondSlop = false + /** Whether touch events are presently occurring within the magnetic field area of a target. */ val objectStuckToTarget: Boolean get() = targetObjectIsStuckTo != null @@ -324,15 +329,32 @@ abstract class MagnetizedObject<T : Any>( // When a gesture begins, recalculate target views' positions on the screen in case they // have changed. Also, clear state. if (ev.action == MotionEvent.ACTION_DOWN) { - updateTargetViewLocations() + updateTargetViews() - // Clear the velocity tracker and assume we're not stuck to a target yet. + // Clear the velocity tracker and stuck target. velocityTracker.clear() targetObjectIsStuckTo = null + + // Set the touch down coordinates and reset movedBeyondSlop. + touchDown.set(ev.rawX, ev.rawY) + movedBeyondSlop = false } + // Always pass events to the VelocityTracker. addMovement(ev) + // If we haven't yet moved beyond the slop distance, check if we have. + if (!movedBeyondSlop) { + val dragDistance = hypot(ev.rawX - touchDown.x, ev.rawY - touchDown.y) + if (dragDistance > touchSlop) { + // If we're beyond the slop distance, save that and continue. + movedBeyondSlop = true + } else { + // Otherwise, don't do anything yet. + return false + } + } + val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target -> val distanceFromTargetCenter = hypot( ev.rawX - target.centerOnScreen.x, @@ -559,8 +581,14 @@ abstract class MagnetizedObject<T : Any>( } /** Updates the locations on screen of all of the [associatedTargets]. */ - internal fun updateTargetViewLocations() { + internal fun updateTargetViews() { associatedTargets.forEach { it.updateLocationOnScreen() } + + // Update the touch slop, since the configuration may have changed. + if (associatedTargets.size > 0) { + touchSlop = + ViewConfiguration.get(associatedTargets[0].targetView.context).scaledTouchSlop + } } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt index f316d0480fac..28a3d6a32a61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt @@ -86,7 +86,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test - fun updateDuration() { + fun updateDurationWithPlayback() { // GIVEN that the duration is contained within the metadata val duration = 12000L val metadata = MediaMetadata.Builder().run { @@ -94,6 +94,12 @@ public class SeekBarViewModelTest : SysuiTestCase() { build() } whenever(mockController.getMetadata()).thenReturn(metadata) + // AND a valid playback state (ie. media session is not destroyed) + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, 200L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) // WHEN the controller is updated viewModel.updateController(mockController, Color.RED) // THEN the duration is extracted @@ -102,6 +108,22 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + fun updateDurationWithoutPlayback() { + // GIVEN that the duration is contained within the metadata + val duration = 12000L + val metadata = MediaMetadata.Builder().run { + putLong(MediaMetadata.METADATA_KEY_DURATION, duration) + build() + } + whenever(mockController.getMetadata()).thenReturn(metadata) + // WHEN the controller is updated + viewModel.updateController(mockController, Color.RED) + // THEN the duration is extracted + assertThat(viewModel.progress.value!!.duration).isEqualTo(duration) + assertThat(viewModel.progress.value!!.enabled).isFalse() + } + + @Test fun updateDurationNegative() { // GIVEN that the duration is negative val duration = -1L @@ -110,6 +132,12 @@ public class SeekBarViewModelTest : SysuiTestCase() { build() } whenever(mockController.getMetadata()).thenReturn(metadata) + // AND a valid playback state (ie. media session is not destroyed) + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, 200L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) // WHEN the controller is updated viewModel.updateController(mockController, Color.RED) // THEN the seek bar is disabled @@ -125,6 +153,12 @@ public class SeekBarViewModelTest : SysuiTestCase() { build() } whenever(mockController.getMetadata()).thenReturn(metadata) + // AND a valid playback state (ie. media session is not destroyed) + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, 200L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) // WHEN the controller is updated viewModel.updateController(mockController, Color.RED) // THEN the seek bar is disabled @@ -372,4 +406,30 @@ public class SeekBarViewModelTest : SysuiTestCase() { // THEN an update task is queued assertThat(fakeExecutor.numPending()).isEqualTo(1) } + + @Test + fun clearSeekBar() { + // GIVEN that the duration is contained within the metadata + val metadata = MediaMetadata.Builder().run { + putLong(MediaMetadata.METADATA_KEY_DURATION, 12000L) + build() + } + whenever(mockController.getMetadata()).thenReturn(metadata) + // AND a valid playback state (ie. media session is not destroyed) + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, 200L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + // AND the controller has been updated + viewModel.updateController(mockController, Color.RED) + // WHEN the controller is cleared on the event when the session is destroyed + viewModel.clearController() + with(fakeExecutor) { + advanceClockToNext() + runAllReady() + } + // THEN the seek bar is disabled + assertThat(viewModel.progress.value!!.enabled).isFalse() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index b6bd5e213dd4..6bcaee100496 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.notification.row; import static android.app.Notification.FLAG_BUBBLE; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; @@ -458,7 +460,9 @@ public class NotificationConversationInfoTest extends SysuiTestCase { } @Test - public void testBindNotification_defaultSelected_notFave_notSilent() { + public void testBindNotification_defaultSelected_notFave_notSilent() throws Exception { + when(mMockINotificationManager.getBubblePreferenceForPackage(anyString(), anyInt())) + .thenReturn(BUBBLE_PREFERENCE_SELECTED); mConversationChannel.setImportance(IMPORTANCE_HIGH); mConversationChannel.setImportantConversation(false); mConversationChannel.setAllowBubbles(true); @@ -476,6 +480,35 @@ public class NotificationConversationInfoTest extends SysuiTestCase { true); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); + assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( + mContext.getString(R.string.notification_channel_summary_default)); + } + + @Test + public void testBindNotification_default_allCanBubble() throws Exception { + when(mMockINotificationManager.getBubblePreferenceForPackage(anyString(), anyInt())) + .thenReturn(BUBBLE_PREFERENCE_ALL); + when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); + mConversationChannel.setImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportantConversation(false); + mConversationChannel.setAllowBubbles(true); + mNotificationInfo.bindNotification( + mShortcutManager, + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mEntry, + null, + null, + mIconFactory, + true); + View view = mNotificationInfo.findViewById(R.id.default_behavior); + assertThat(view.isSelected()).isTrue(); + assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( + mContext.getString(R.string.notification_channel_summary_default_with_bubbles, + "App Name")); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt index f6b7b74d4bfc..251ca9c8dcb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt @@ -186,8 +186,8 @@ class MagnetizedObjectTest : SysuiTestCase() { @Test fun testMotionEventConsumption_downInMagneticField() { - // We should consume DOWN events if they occur in the field. - assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + // We should not consume DOWN events even if they occur in the field. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN))) } @@ -342,10 +342,14 @@ class MagnetizedObjectTest : SysuiTestCase() { // Trigger the magnet animation, and block the test until it ends. PhysicsAnimatorTestUtils.setAllAnimationsBlock(true) magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( - x = targetCenterX, - y = targetCenterY, + x = targetCenterX - 250, + y = targetCenterY - 250, action = MotionEvent.ACTION_DOWN)) + magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX, + y = targetCenterY)) + // The object's (top-left) position should now position it centered over the target. assertEquals(targetCenterX - objectSize / 2, objectX) assertEquals(targetCenterY - objectSize / 2, objectY) diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml index 1dc8227e81f4..2b2fe4534c3e 100644 --- a/packages/Tethering/AndroidManifest.xml +++ b/packages/Tethering/AndroidManifest.xml @@ -34,11 +34,14 @@ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> + <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.TETHER_PRIVILEGED" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + <protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" /> + <application android:process="com.android.networkstack.process" android:extractNativeLibs="false" diff --git a/packages/Tethering/res/values-mcc204-mnc04/strings.xml b/packages/Tethering/res/values-mcc204-mnc04/strings.xml deleted file mode 100644 index 9dadd49cf8a4..000000000000 --- a/packages/Tethering/res/values-mcc204-mnc04/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- String for no upstream notification title [CHAR LIMIT=200] --> - <string name="no_upstream_notification_title">Tethering has no internet</string> - <!-- String for no upstream notification title [CHAR LIMIT=200] --> - <string name="no_upstream_notification_message">Devices can\u2019t connect</string> - <!-- String for no upstream notification disable button [CHAR LIMIT=200] --> - <string name="no_upstream_notification_disable_button">Turn off tethering</string> - - <!-- String for cellular roaming notification title [CHAR LIMIT=200] --> - <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string> - <!-- String for cellular roaming notification message [CHAR LIMIT=500] --> - <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string> - <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] --> - <string name="upstream_roaming_notification_continue_button">Continue</string> -</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml new file mode 100644 index 000000000000..8c627d5df058 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004/config.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to + "0" for disable this feature. --> + <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer> +</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml new file mode 100644 index 000000000000..8c627d5df058 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480/config.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to + "0" for disable this feature. --> + <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer> +</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index 430fdc42284d..52aa5bbaffa5 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -202,4 +202,10 @@ <string name="tethering_notification_title">@string/tethered_notification_title</string> <!-- String for tether enable notification message. --> <string name="tethering_notification_message">@string/tethered_notification_message</string> + + <!-- No upstream notification is shown when there is a downstream but no upstream that is able + to do the tethering. --> + <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to + "-1" for disable this feature. --> + <integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer> </resources> diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 4c7b2d49ee9a..049a9f68bbd2 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -38,8 +38,6 @@ import android.content.IntentFilter; import android.net.util.SharedLog; import android.os.Bundle; import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.os.Parcel; import android.os.PersistableBundle; import android.os.ResultReceiver; @@ -75,11 +73,6 @@ public class EntitlementManager { private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; - private static final int EVENT_START_PROVISIONING = 0; - private static final int EVENT_STOP_PROVISIONING = 1; - private static final int EVENT_UPSTREAM_CHANGED = 2; - private static final int EVENT_MAYBE_RUN_PROVISIONING = 3; - private static final int EVENT_GET_ENTITLEMENT_VALUE = 4; // The ArraySet contains enabled downstream types, ex: // {@link TetheringManager.TETHERING_WIFI} @@ -90,7 +83,7 @@ public class EntitlementManager { private final int mPermissionChangeMessageCode; private final SharedLog mLog; private final SparseIntArray mEntitlementCacheValue; - private final EntitlementHandler mHandler; + private final Handler mHandler; private final StateMachine mTetherMasterSM; // Key: TetheringManager.TETHERING_*(downstream). // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). @@ -112,10 +105,7 @@ public class EntitlementManager { mEntitlementCacheValue = new SparseIntArray(); mTetherMasterSM = tetherMasterSM; mPermissionChangeMessageCode = permissionChangeMessageCode; - final Handler masterHandler = tetherMasterSM.getHandler(); - // Create entitlement's own handler which is associated with TetherMaster thread - // let all entitlement processes run in the same thread. - mHandler = new EntitlementHandler(masterHandler.getLooper()); + mHandler = tetherMasterSM.getHandler(); mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), null, mHandler); mSilentProvisioningService = ComponentName.unflattenFromString( @@ -172,14 +162,9 @@ public class EntitlementManager { * provisioning app UI if there is one. */ public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_START_PROVISIONING, - downstreamType, encodeBool(showProvisioningUi))); - } - - private void handleStartProvisioningIfNeeded(int type, boolean showProvisioningUi) { - if (!isValidDownstreamType(type)) return; + if (!isValidDownstreamType(downstreamType)) return; - if (!mCurrentTethers.contains(type)) mCurrentTethers.add(type); + if (!mCurrentTethers.contains(downstreamType)) mCurrentTethers.add(downstreamType); final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (isTetherProvisioningRequired(config)) { @@ -192,9 +177,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(type, config.activeDataSubId); + runUiTetherProvisioning(downstreamType, config.activeDataSubId); } else { - runSilentTetherProvisioning(type, config.activeDataSubId); + runSilentTetherProvisioning(downstreamType, config.activeDataSubId); } mNeedReRunProvisioningUi = false; } else { @@ -211,10 +196,6 @@ public class EntitlementManager { * @param type tethering type from TetheringManager.TETHERING_{@code *} */ public void stopProvisioningIfNeeded(int type) { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0)); - } - - private void handleStopProvisioningIfNeeded(int type) { if (!isValidDownstreamType(type)) return; mCurrentTethers.remove(type); @@ -230,11 +211,6 @@ public class EntitlementManager { * @param isCellular whether tethering upstream is cellular. */ public void notifyUpstream(boolean isCellular) { - mHandler.sendMessage(mHandler.obtainMessage( - EVENT_UPSTREAM_CHANGED, encodeBool(isCellular), 0)); - } - - private void handleNotifyUpstream(boolean isCellular) { if (DBG) { mLog.i("notifyUpstream: " + isCellular + ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted @@ -244,16 +220,17 @@ public class EntitlementManager { if (mUsingCellularAsUpstream) { final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - handleMaybeRunProvisioning(config); + maybeRunProvisioning(config); } } /** Run provisioning if needed */ public void maybeRunProvisioning() { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_MAYBE_RUN_PROVISIONING)); + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + maybeRunProvisioning(config); } - private void handleMaybeRunProvisioning(final TetheringConfiguration config) { + private void maybeRunProvisioning(final TetheringConfiguration config) { if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) { return; } @@ -319,7 +296,7 @@ public class EntitlementManager { } if (mUsingCellularAsUpstream) { - handleMaybeRunProvisioning(config); + maybeRunProvisioning(config); } } @@ -494,46 +471,6 @@ public class EntitlementManager { } }; - private class EntitlementHandler extends Handler { - EntitlementHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case EVENT_START_PROVISIONING: - handleStartProvisioningIfNeeded(msg.arg1, toBool(msg.arg2)); - break; - case EVENT_STOP_PROVISIONING: - handleStopProvisioningIfNeeded(msg.arg1); - break; - case EVENT_UPSTREAM_CHANGED: - handleNotifyUpstream(toBool(msg.arg1)); - break; - case EVENT_MAYBE_RUN_PROVISIONING: - final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); - handleMaybeRunProvisioning(config); - break; - case EVENT_GET_ENTITLEMENT_VALUE: - handleRequestLatestTetheringEntitlementValue(msg.arg1, - (ResultReceiver) msg.obj, toBool(msg.arg2)); - break; - default: - mLog.log("Unknown event: " + msg.what); - break; - } - } - } - - private static boolean toBool(int encodedBoolean) { - return encodedBoolean != 0; - } - - private static int encodeBool(boolean b) { - return b ? 1 : 0; - } - private static boolean isValidDownstreamType(int type) { switch (type) { case TETHERING_BLUETOOTH: @@ -644,13 +581,6 @@ public class EntitlementManager { /** Get the last value of the tethering entitlement check. */ public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, boolean showEntitlementUi) { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE, - downstream, encodeBool(showEntitlementUi), receiver)); - - } - - private void handleRequestLatestTetheringEntitlementValue(int downstream, - ResultReceiver receiver, boolean showEntitlementUi) { if (!isValidDownstreamType(downstream)) { receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null); return; diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java index f3cead92be7e..da8bf54718e9 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -257,7 +257,7 @@ public class Tethering { mContext = mDeps.getContext(); mNetd = mDeps.getINetd(mContext); mLooper = mDeps.getTetheringLooper(); - mNotificationUpdater = mDeps.getNotificationUpdater(mContext); + mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper); mPublicSync = new Object(); @@ -337,6 +337,11 @@ public class Tethering { filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); mContext.registerReceiver(mStateReceiver, filter, null, mHandler); + final IntentFilter noUpstreamFilter = new IntentFilter(); + noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING); + mContext.registerReceiver( + mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler); + final WifiManager wifiManager = getWifiManager(); if (wifiManager != null) { wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); @@ -855,6 +860,8 @@ public class Tethering { } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) { mLog.log("OBSERVED data saver changed"); handleDataSaverChanged(); + } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) { + untetherAll(); } } @@ -922,8 +929,10 @@ public class Tethering { case WifiManager.WIFI_AP_STATE_ENABLED: enableWifiIpServingLocked(ifname, ipmode); break; - case WifiManager.WIFI_AP_STATE_DISABLED: case WifiManager.WIFI_AP_STATE_DISABLING: + // We can see this state on the way to disabled. + break; + case WifiManager.WIFI_AP_STATE_DISABLED: case WifiManager.WIFI_AP_STATE_FAILED: default: disableWifiIpServingLocked(ifname, curState); @@ -1944,10 +1953,12 @@ public class Tethering { /** Get the latest value of the tethering entitlement check. */ void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver, boolean showEntitlementUi) { - if (receiver != null) { + if (receiver == null) return; + + mHandler.post(() -> { mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); - } + }); } /** Register tethering event callback */ @@ -2011,6 +2022,7 @@ public class Tethering { } finally { mTetheringEventCallbacks.finishBroadcast(); } + mNotificationUpdater.onUpstreamNetworkChanged(network); } private void reportConfigurationChanged(TetheringConfigurationParcel config) { diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index 893c5823dce1..9b54b5ff2403 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -106,8 +106,9 @@ public abstract class TetheringDependencies { /** * Get a reference to the TetheringNotificationUpdater to be used by tethering. */ - public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) { - return new TetheringNotificationUpdater(ctx); + public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx, + @NonNull final Looper looper) { + return new TetheringNotificationUpdater(ctx, looper); } /** diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index 42870560cb5e..ff83fd1e4f1e 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -19,18 +19,25 @@ package com.android.networkstack.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; +import static android.text.TextUtils.isEmpty; import android.app.Notification; +import android.app.Notification.Action; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.content.res.Resources; +import android.net.Network; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.UserHandle; import android.provider.Settings; import android.telephony.SubscriptionManager; -import android.text.TextUtils; +import android.telephony.TelephonyManager; import android.util.Log; import android.util.SparseArray; @@ -39,9 +46,13 @@ import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import java.util.Arrays; +import java.util.List; + /** * A class to display tethering-related notifications. * @@ -58,12 +69,22 @@ public class TetheringNotificationUpdater { private static final String WIFI_DOWNSTREAM = "WIFI"; private static final String USB_DOWNSTREAM = "USB"; private static final String BLUETOOTH_DOWNSTREAM = "BT"; + @VisibleForTesting + static final String ACTION_DISABLE_TETHERING = + "com.android.server.connectivity.tethering.DISABLE_TETHERING"; private static final boolean NOTIFY_DONE = true; private static final boolean NO_NOTIFY = false; - // Id to update and cancel tethering notification. Must be unique within the tethering app. - private static final int ENABLE_NOTIFICATION_ID = 1000; + @VisibleForTesting + static final int EVENT_SHOW_NO_UPSTREAM = 1; + // Id to update and cancel enable notification. Must be unique within the tethering app. + @VisibleForTesting + static final int ENABLE_NOTIFICATION_ID = 1000; // Id to update and cancel restricted notification. Must be unique within the tethering app. - private static final int RESTRICTED_NOTIFICATION_ID = 1001; + @VisibleForTesting + static final int RESTRICTED_NOTIFICATION_ID = 1001; + // Id to update and cancel no upstream notification. Must be unique within the tethering app. + @VisibleForTesting + static final int NO_UPSTREAM_NOTIFICATION_ID = 1002; @VisibleForTesting static final int NO_ICON_ID = 0; @VisibleForTesting @@ -71,14 +92,16 @@ public class TetheringNotificationUpdater { private final Context mContext; private final NotificationManager mNotificationManager; private final NotificationChannel mChannel; + private final Handler mHandler; // WARNING : the constructor is called on a different thread. Thread safety therefore - // relies on this value being initialized to 0, and not any other value. If you need + // relies on these values being initialized to 0 or false, and not any other value. If you need // to change this, you will need to change the thread where the constructor is invoked, // or to introduce synchronization. // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. // This value has to be made 1 2 and 4, and OR'd with the others. private int mDownstreamTypesMask = DOWNSTREAM_NONE; + private boolean mNoUpstream = false; // WARNING : this value is not able to being initialized to 0 and must have volatile because // telephony service is not guaranteed that is up before tethering service starts. If telephony @@ -87,10 +110,30 @@ public class TetheringNotificationUpdater { // INVALID_SUBSCRIPTION_ID. private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID}) + @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID}) @interface NotificationId {} - public TetheringNotificationUpdater(@NonNull final Context context) { + private static final class MccMncOverrideInfo { + public final List<String> visitedMccMncs; + public final int homeMcc; + public final int homeMnc; + MccMncOverrideInfo(List<String> visitedMccMncs, int mcc, int mnc) { + this.visitedMccMncs = visitedMccMncs; + this.homeMcc = mcc; + this.homeMnc = mnc; + } + } + + private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>(); + + static { + // VZW + sCarrierIdToMccMnc.put( + 1839, new MccMncOverrideInfo(Arrays.asList(new String[] {"20404"}), 311, 480)); + } + + public TetheringNotificationUpdater(@NonNull final Context context, + @NonNull final Looper looper) { mContext = context; mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) .getSystemService(Context.NOTIFICATION_SERVICE); @@ -99,6 +142,22 @@ public class TetheringNotificationUpdater { context.getResources().getString(R.string.notification_channel_tethering_status), NotificationManager.IMPORTANCE_LOW); mNotificationManager.createNotificationChannel(mChannel); + mHandler = new NotificationHandler(looper); + } + + private class NotificationHandler extends Handler { + NotificationHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case EVENT_SHOW_NO_UPSTREAM: + notifyTetheringNoUpstream(); + break; + } + } } /** Called when downstream has changed */ @@ -106,6 +165,7 @@ public class TetheringNotificationUpdater { if (mDownstreamTypesMask == downstreamTypesMask) return; mDownstreamTypesMask = downstreamTypesMask; updateEnableNotification(); + updateNoUpstreamNotification(); } /** Called when active data subscription id changed */ @@ -113,21 +173,62 @@ public class TetheringNotificationUpdater { if (mActiveDataSubId == subId) return; mActiveDataSubId = subId; updateEnableNotification(); + updateNoUpstreamNotification(); } + /** Called when upstream network changed */ + public void onUpstreamNetworkChanged(@Nullable final Network network) { + final boolean isNoUpstream = (network == null); + if (mNoUpstream == isNoUpstream) return; + mNoUpstream = isNoUpstream; + updateNoUpstreamNotification(); + } + + @NonNull @VisibleForTesting - Resources getResourcesForSubId(@NonNull final Context c, final int subId) { - return SubscriptionManager.getResourcesForSubId(c, subId); + final Handler getHandler() { + return mHandler; + } + + @NonNull + @VisibleForTesting + Resources getResourcesForSubId(@NonNull final Context context, final int subId) { + final Resources res = SubscriptionManager.getResourcesForSubId(context, subId); + final TelephonyManager tm = + ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)) + .createForSubscriptionId(mActiveDataSubId); + final int carrierId = tm.getSimCarrierId(); + final String mccmnc = tm.getSimOperator(); + final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId); + if (overrideInfo != null && overrideInfo.visitedMccMncs.contains(mccmnc)) { + // Re-configure MCC/MNC value to specific carrier to get right resources. + final Configuration config = res.getConfiguration(); + config.mcc = overrideInfo.homeMcc; + config.mnc = overrideInfo.homeMnc; + return context.createConfigurationContext(config).getResources(); + } + return res; } private void updateEnableNotification() { - final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; + final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; if (tetheringInactive || setupNotification() == NO_NOTIFY) { clearNotification(ENABLE_NOTIFICATION_ID); } } + private void updateNoUpstreamNotification() { + final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; + + if (tetheringInactive + || !mNoUpstream + || setupNoUpstreamNotification() == NO_NOTIFY) { + clearNotification(NO_UPSTREAM_NOTIFICATION_ID); + mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM); + } + } + @VisibleForTesting void tetheringRestrictionLifted() { clearNotification(RESTRICTED_NOTIFICATION_ID); @@ -142,9 +243,38 @@ public class TetheringNotificationUpdater { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); final String title = res.getString(R.string.disable_tether_notification_title); final String message = res.getString(R.string.disable_tether_notification_message); + if (isEmpty(title) || isEmpty(message)) return; + + final PendingIntent pi = PendingIntent.getActivity( + mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), + 0 /* requestCode */, + new Intent(Settings.ACTION_TETHER_SETTINGS), + Intent.FLAG_ACTIVITY_NEW_TASK, + null /* options */); + + showNotification(R.drawable.stat_sys_tether_general, title, message, + RESTRICTED_NOTIFICATION_ID, pi, new Action[0]); + } + + private void notifyTetheringNoUpstream() { + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final String title = res.getString(R.string.no_upstream_notification_title); + final String message = res.getString(R.string.no_upstream_notification_message); + final String disableButton = + res.getString(R.string.no_upstream_notification_disable_button); + if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return; + + final Intent intent = new Intent(ACTION_DISABLE_TETHERING); + intent.setPackage(mContext.getPackageName()); + final PendingIntent pi = PendingIntent.getBroadcast( + mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), + 0 /* requestCode */, + intent, + 0 /* flags */); + final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build(); showNotification(R.drawable.stat_sys_tether_general, title, message, - RESTRICTED_NOTIFICATION_ID); + NO_UPSTREAM_NOTIFICATION_ID, null /* pendingIntent */, action); } /** @@ -179,12 +309,13 @@ public class TetheringNotificationUpdater { * * @return {@link android.util.SparseArray} with downstream types and icon id info. */ + @NonNull @VisibleForTesting SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) { final String[] array = res.getStringArray(id); final SparseArray<Integer> icons = new SparseArray<>(); for (String config : array) { - if (TextUtils.isEmpty(config)) continue; + if (isEmpty(config)) continue; final String[] elements = config.split(";"); if (elements.length != 2) { @@ -204,6 +335,18 @@ public class TetheringNotificationUpdater { return icons; } + private boolean setupNoUpstreamNotification() { + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final int delayToShowUpstreamNotification = + res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul); + + if (delayToShowUpstreamNotification < 0) return NO_NOTIFY; + + mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM), + delayToShowUpstreamNotification); + return NOTIFY_DONE; + } + private boolean setupNotification() { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); final SparseArray<Integer> downstreamIcons = @@ -214,17 +357,22 @@ public class TetheringNotificationUpdater { final String title = res.getString(R.string.tethering_notification_title); final String message = res.getString(R.string.tethering_notification_message); + if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY; - showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID); + final PendingIntent pi = PendingIntent.getActivity( + mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), + 0 /* requestCode */, + new Intent(Settings.ACTION_TETHER_SETTINGS), + Intent.FLAG_ACTIVITY_NEW_TASK, + null /* options */); + + showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID, pi, new Action[0]); return NOTIFY_DONE; } private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message, @NotificationId final int id) { - final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS); - final PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0), - 0 /* requestCode */, intent, 0 /* flags */, null /* options */); + @NonNull final String message, @NotificationId final int id, @Nullable PendingIntent pi, + @NonNull final Action... actions) { final Notification notification = new Notification.Builder(mContext, mChannel.getId()) .setSmallIcon(iconId) @@ -236,6 +384,7 @@ public class TetheringNotificationUpdater { .setVisibility(Notification.VISIBILITY_PUBLIC) .setCategory(Notification.CATEGORY_STATUS) .setContentIntent(pi) + .setActions(actions) .build(); mNotificationManager.notify(null /* tag */, id, notification); diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml index 55640db69324..31eaabff5274 100644 --- a/packages/Tethering/tests/unit/AndroidManifest.xml +++ b/packages/Tethering/tests/unit/AndroidManifest.xml @@ -16,6 +16,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack.tethering.tests.unit"> + <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.TETHER_PRIVILEGED"/> <application android:debuggable="true"> diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index 7bff74b25d94..5f8858857c75 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -23,14 +23,26 @@ import android.content.res.Resources import android.net.ConnectivityManager.TETHERING_BLUETOOTH import android.net.ConnectivityManager.TETHERING_USB import android.net.ConnectivityManager.TETHERING_WIFI +import android.net.Network +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper import android.os.UserHandle import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID -import androidx.test.platform.app.InstrumentationRegistry +import android.telephony.TelephonyManager import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE +import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NOTIFICATION_ID +import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM +import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID +import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID +import com.android.testutils.waitForIdle +import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.fail import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -43,8 +55,8 @@ import org.mockito.Mockito.doReturn import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.times -import org.mockito.Mockito.verifyZeroInteractions import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations const val TEST_SUBID = 1 @@ -55,10 +67,13 @@ const val GENERAL_ICON_ID = 4 const val WIFI_MASK = 1 shl TETHERING_WIFI const val USB_MASK = 1 shl TETHERING_USB const val BT_MASK = 1 shl TETHERING_BLUETOOTH -const val TITTLE = "Tethering active" +const val TITLE = "Tethering active" const val MESSAGE = "Tap here to set up." -const val TEST_TITTLE = "Hotspot active" +const val TEST_TITLE = "Hotspot active" const val TEST_MESSAGE = "Tap to set up hotspot." +const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access" +const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet." +const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot" @RunWith(AndroidJUnit4::class) @SmallTest @@ -67,12 +82,15 @@ class TetheringNotificationUpdaterTest { // should crash if they are used before being initialized. @Mock private lateinit var mockContext: Context @Mock private lateinit var notificationManager: NotificationManager + @Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var defaultResources: Resources @Mock private lateinit var testResources: Resources - // lateinit for this class under test, as it should be reset to a different instance for every - // tests but should always be initialized before use (or the test should crash). + // lateinit for these classes under test, as they should be reset to a different instance for + // every test but should always be initialized before use (or the test should crash). + private lateinit var context: TestContext private lateinit var notificationUpdater: TetheringNotificationUpdater + private lateinit var fakeTetheringThread: HandlerThread private val ENABLE_ICON_CONFIGS = arrayOf( "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth", @@ -82,11 +100,19 @@ class TetheringNotificationUpdaterTest { private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { override fun createContextAsUser(user: UserHandle, flags: Int) = if (user == UserHandle.ALL) mockContext else this + override fun getSystemService(name: String) = + if (name == Context.TELEPHONY_SERVICE) telephonyManager + else super.getSystemService(name) } - private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) { + private inner class WrappedNotificationUpdater(c: Context, looper: Looper) + : TetheringNotificationUpdater(c, looper) { override fun getResourcesForSubId(context: Context, subId: Int) = - if (subId == TEST_SUBID) testResources else defaultResources + when (subId) { + TEST_SUBID -> testResources + INVALID_SUBSCRIPTION_ID -> defaultResources + else -> super.getResourcesForSubId(context, subId) + } } private fun setupResources() { @@ -94,12 +120,20 @@ class TetheringNotificationUpdaterTest { .getStringArray(R.array.tethering_notification_icons) doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources) .getStringArray(R.array.tethering_notification_icons) - doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title) + doReturn(5).`when`(testResources) + .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) + doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title) doReturn(MESSAGE).`when`(defaultResources) .getString(R.string.tethering_notification_message) - doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title) + doReturn(TEST_TITLE).`when`(testResources).getString(R.string.tethering_notification_title) doReturn(TEST_MESSAGE).`when`(testResources) .getString(R.string.tethering_notification_message) + doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources) + .getString(R.string.no_upstream_notification_title) + doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources) + .getString(R.string.no_upstream_notification_message) + doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources) + .getString(R.string.no_upstream_notification_disable_button) doReturn(USB_ICON_ID).`when`(defaultResources) .getIdentifier(eq("android.test:drawable/usb"), any(), any()) doReturn(BT_ICON_ID).`when`(defaultResources) @@ -113,35 +147,61 @@ class TetheringNotificationUpdaterTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) - val context = TestContext(InstrumentationRegistry.getInstrumentation().context) + context = TestContext(InstrumentationRegistry.getInstrumentation().context) doReturn(notificationManager).`when`(mockContext) .getSystemService(Context.NOTIFICATION_SERVICE) - notificationUpdater = WrappedNotificationUpdater(context) + fakeTetheringThread = HandlerThread(this::class.simpleName) + fakeTetheringThread.start() + notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper) setupResources() } + @After + fun tearDown() { + fakeTetheringThread.quitSafely() + } + private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE) private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT) - private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") { - verify(notificationManager, never()).cancel(any(), anyInt()) + private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) { + verify(notificationManager, never()).cancel(any(), eq(id)) val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) verify(notificationManager, times(1)) - .notify(any(), anyInt(), notificationCaptor.capture()) + .notify(any(), eq(id), notificationCaptor.capture()) val notification = notificationCaptor.getValue() assertEquals(iconId, notification.smallIcon.resId) assertEquals(title, notification.title()) assertEquals(text, notification.text()) + } + + private fun verifyNotificationCancelled(id: Int) = + verify(notificationManager, times(1)).cancel(any(), eq(id)) + private val tetheringActiveNotifications = + listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID) + + private fun verifyCancelAllTetheringActiveNotifications() { + tetheringActiveNotifications.forEach { + verifyNotificationCancelled(it) + } reset(notificationManager) } - private fun verifyNoNotification() { - verify(notificationManager, times(1)).cancel(any(), anyInt()) - verify(notificationManager, never()).notify(any(), anyInt(), any()) - + private fun verifyOnlyTetheringActiveNotification( + notifyId: Int, + iconId: Int, + title: String, + text: String + ) { + tetheringActiveNotifications.forEach { + when (it) { + notifyId -> verifyNotification(iconId, title, text, notifyId) + else -> verifyNotificationCancelled(it) + } + } reset(notificationManager) } @@ -149,7 +209,7 @@ class TetheringNotificationUpdaterTest { fun testNotificationWithDownstreamChanged() { // Wifi downstream. No notification. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() // Same downstream changed. Nothing happened. notificationUpdater.onDownstreamChanged(WIFI_MASK) @@ -157,22 +217,23 @@ class TetheringNotificationUpdaterTest { // Wifi and usb downstreams. Show enable notification notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) - verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE) + verifyOnlyTetheringActiveNotification( + ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE) // Usb downstream. Still show enable notification. notificationUpdater.onDownstreamChanged(USB_MASK) - verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE) // No downstream. No notification. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() } @Test fun testNotificationWithActiveDataSubscriptionIdChanged() { // Usb downstream. Showed enable notification with default resource. notificationUpdater.onDownstreamChanged(USB_MASK) - verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE) // Same subId changed. Nothing happened. notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) @@ -180,15 +241,16 @@ class TetheringNotificationUpdaterTest { // Set test sub id. Clear notification with test resource. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() // Wifi downstream. Show enable notification with test resource. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE) + verifyOnlyTetheringActiveNotification( + ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) // No downstream. No notification. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() } private fun assertIconNumbers(number: Int, configs: Array<String?>) { @@ -227,10 +289,8 @@ class TetheringNotificationUpdaterTest { @Test fun testSetupRestrictedNotification() { - val title = InstrumentationRegistry.getInstrumentation().context.resources - .getString(R.string.disable_tether_notification_title) - val message = InstrumentationRegistry.getInstrumentation().context.resources - .getString(R.string.disable_tether_notification_message) + val title = context.resources.getString(R.string.disable_tether_notification_title) + val message = context.resources.getString(R.string.disable_tether_notification_message) val disallowTitle = "Tether function is disallowed" val disallowMessage = "Please contact your admin" doReturn(title).`when`(defaultResources) @@ -244,18 +304,127 @@ class TetheringNotificationUpdaterTest { // User restrictions on. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(R.drawable.stat_sys_tether_general, title, message) + verifyNotification(R.drawable.stat_sys_tether_general, title, message, + RESTRICTED_NOTIFICATION_ID) + reset(notificationManager) // User restrictions off. Clear notification. notificationUpdater.tetheringRestrictionLifted() - verifyNoNotification() + verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID) + reset(notificationManager) // Set test sub id. No notification. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNoNotification() + verifyCancelAllTetheringActiveNotifications() // User restrictions on again. Show restricted notification with test resource. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) + verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage, + RESTRICTED_NOTIFICATION_ID) + reset(notificationManager) + } + + val MAX_BACKOFF_MS = 200L + /** + * Waits for all messages, including delayed ones, to be processed. + * + * This will wait until the handler has no more messages to be processed including + * delayed ones, or the timeout has expired. It uses an exponential backoff strategy + * to wait longer and longer to consume less CPU, with the max granularity being + * MAX_BACKOFF_MS. + * + * @return true if all messages have been processed including delayed ones, false if timeout + * + * TODO: Move this method to com.android.testutils.HandlerUtils.kt. + */ + private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) { + fun hasMatchingMessages() = + if (what == null) hasMessagesOrCallbacks() else hasMessages(what) + val expiry = System.currentTimeMillis() + timeoutMs + var delay = 5L + while (System.currentTimeMillis() < expiry && hasMatchingMessages()) { + // None of Handler, Looper, Message and MessageQueue expose any way to retrieve + // the time when the next (let alone the last) message will be processed, so + // short of examining the internals with reflection sleep() is the only solution. + Thread.sleep(delay) + delay = (delay * 2) + .coerceAtMost(expiry - System.currentTimeMillis()) + .coerceAtMost(MAX_BACKOFF_MS) + } + + val timeout = expiry - System.currentTimeMillis() + if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms") + waitForIdle(timeout) + } + + @Test + fun testNotificationWithUpstreamNetworkChanged() { + // Set test sub id. No notification. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyCancelAllTetheringActiveNotifications() + + // Wifi downstream. Show enable notification with test resource. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyOnlyTetheringActiveNotification( + ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) + + // There is no upstream. Show no upstream notification. + notificationUpdater.onUpstreamNetworkChanged(null) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) + verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE, + TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) + reset(notificationManager) + + // Same upstream network changed. Nothing happened. + notificationUpdater.onUpstreamNetworkChanged(null) + verifyZeroInteractions(notificationManager) + + // Upstream come back. Clear no upstream notification. + notificationUpdater.onUpstreamNetworkChanged(Network(1000)) + verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID) + reset(notificationManager) + + // No upstream again. Show no upstream notification. + notificationUpdater.onUpstreamNetworkChanged(null) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) + verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE, + TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) + reset(notificationManager) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyCancelAllTetheringActiveNotifications() + + // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream + // again. Show enable notification only. + doReturn(-1).`when`(testResources) + .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) + notificationUpdater.onDownstreamChanged(WIFI_MASK) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) + verifyOnlyTetheringActiveNotification( + ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) + } + + @Test + fun testGetResourcesForSubId() { + doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt()) + doReturn(1234).`when`(telephonyManager).getSimCarrierId() + doReturn("000000").`when`(telephonyManager).getSimOperator() + + val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId. + val config = context.resources.configuration + var res = notificationUpdater.getResourcesForSubId(context, subId) + assertEquals(config.mcc, res.configuration.mcc) + assertEquals(config.mnc, res.configuration.mnc) + + doReturn(1839).`when`(telephonyManager).getSimCarrierId() + res = notificationUpdater.getResourcesForSubId(context, subId) + assertEquals(config.mcc, res.configuration.mcc) + assertEquals(config.mnc, res.configuration.mnc) + + doReturn("20404").`when`(telephonyManager).getSimOperator() + res = notificationUpdater.getResourcesForSubId(context, subId) + assertEquals(311, res.configuration.mcc) + assertEquals(480, res.configuration.mnc) } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index d4be3a26d958..feb99e6b248d 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -383,7 +383,7 @@ public class TetheringTest { } @Override - public TetheringNotificationUpdater getNotificationUpdater(Context ctx) { + public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) { return mNotificationUpdater; } } @@ -1691,6 +1691,18 @@ public class TetheringTest { assertEquals(clientAddrParceled, params.clientAddr); } + @Test + public void testUpstreamNetworkChanged() { + final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) + mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; + final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + stateMachine.chooseUpstreamType(true); + + verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network)); + verify(mNotificationUpdater, times(1)).onUpstreamNetworkChanged(eq(upstreamState.network)); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 7230b00f87ad..d252f9e69a22 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2441,7 +2441,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * accessibility button. * 2) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk * version <= Q: turns on / off the accessibility service. - * 3) For services targeting sdk version > Q: + * 3) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk + * version > Q and request accessibility button: turn on the accessibility service if it's + * not in the enabled state. + * (It'll happen when a service is disabled and assigned to shortcut then upgraded.) + * 4) For services targeting sdk version > Q: * a) Turns on / off the accessibility service, if service does not request accessibility * button. * b) Callbacks to accessibility service if service is bounded and requests accessibility @@ -2475,6 +2479,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } return true; } + if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY && targetSdk > Build.VERSION_CODES.Q + && requestA11yButton) { + if (!userState.getEnabledServicesLocked().contains(assignedTarget)) { + enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId); + return true; + } + } // Callbacks to a11y service if it's bounded and requests a11y button. if (serviceConnection == null || !userState.mBoundServices.contains(serviceConnection) diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 3d6861898aaf..9d1ad4239a24 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2653,6 +2653,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } else if (viewState.id.equals(this.mCurrentViewId) && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) { requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText); + } else if (viewState.id.equals(this.mCurrentViewId) + && (viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { + if (!TextUtils.isEmpty(filterText)) { + mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); + } } viewState.setState(ViewState.STATE_CHANGED); diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 31bcceaba889..8ecda8f1a131 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -2830,7 +2830,6 @@ public class AppOpsService extends IAppOpsService.Stub { private int checkOperationImpl(int code, int uid, String packageName, boolean raw) { - verifyIncomingUid(uid); verifyIncomingOp(code); String resolvedPackageName = resolvePackageName(uid, packageName); if (resolvedPackageName == null) { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index a87fb8b5c301..b4f7cdbd5694 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -552,7 +552,8 @@ public final class DisplayManagerService extends SystemService { } if (state == Display.STATE_OFF) { brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT; - } else if (brightnessState < PowerManager.BRIGHTNESS_MIN || Float.isNaN(brightnessState)) { + } else if (brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT + && brightnessState < PowerManager.BRIGHTNESS_MIN) { brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; } else if (brightnessState > PowerManager.BRIGHTNESS_MAX) { brightnessState = PowerManager.BRIGHTNESS_MAX; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index e6cb37185d71..b949d6bcf2e2 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -784,6 +784,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final AtomicInteger sSequenceNumber = new AtomicInteger(0); private static final class Entry { + final int mSequenceNumber = sSequenceNumber.getAndIncrement(); final ClientState mClientState; @SoftInputModeFlags final int mFocusedWindowSoftInputMode; @@ -831,7 +832,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub continue; } pw.print(prefix); - pw.println("SoftInputShowHideHistory #" + sSequenceNumber.getAndIncrement() + ":"); + pw.println("SoftInputShowHideHistory #" + entry.mSequenceNumber + ":"); pw.print(prefix); pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime)) diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java index 195b059b7374..095cd146da4f 100644 --- a/services/core/java/com/android/server/location/GeofenceManager.java +++ b/services/core/java/com/android/server/location/GeofenceManager.java @@ -253,7 +253,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish int op = CallerIdentity.asAppOp(identity.permissionLevel); if (op >= 0) { if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity.uid, - identity.packageName, identity.featureId, null) + identity.packageName, identity.featureId, identity.listenerId) != AppOpsManager.MODE_ALLOWED) { continue; } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 6e2feeb15e21..1345e3759d2f 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -261,8 +261,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { .build(); builder.addSelectedRoute(mSelectedRouteId); - for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) { - builder.addTransferableRoute(route.getId()); + if (mBtRouteProvider != null) { + for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) { + builder.addTransferableRoute(route.getId()); + } } RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build(); diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index aed29272cada..e98326b620b2 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -30,6 +30,7 @@ import android.provider.Settings; import android.service.notification.Condition; import android.service.notification.ConditionProviderService; import android.service.notification.IConditionProvider; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -54,7 +55,6 @@ public class ConditionProviders extends ManagedServices { private final ArraySet<String> mSystemConditionProviderNames; private final ArraySet<SystemConditionProviderService> mSystemConditionProviders = new ArraySet<>(); - private Callback mCallback; public ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm) { @@ -195,6 +195,21 @@ public class ConditionProviders extends ManagedServices { } @Override + protected void loadDefaultsFromConfig() { + String defaultDndAccess = mContext.getResources().getString( + R.string.config_defaultDndAccessPackages); + if (defaultDndAccess != null) { + String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR); + for (int i = 0; i < dnds.length; i++) { + if (TextUtils.isEmpty(dnds[i])) { + continue; + } + addDefaultComponentOrPackage(dnds[i]); + } + } + } + + @Override protected void onServiceRemovedLocked(ManagedServiceInfo removed) { if (removed == null) return; for (int i = mRecords.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 45df3686d056..5d3dc5f19714 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -21,6 +21,7 @@ import static android.content.Context.BIND_AUTO_CREATE; import static android.content.Context.BIND_FOREGROUND_SERVICE; import static android.content.Context.DEVICE_POLICY_SERVICE; import static android.os.UserHandle.USER_ALL; +import static android.os.UserHandle.USER_SYSTEM; import android.annotation.NonNull; import android.app.ActivityManager; @@ -96,6 +97,8 @@ abstract public class ManagedServices { private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000; protected static final String ENABLED_SERVICES_SEPARATOR = ":"; + private static final String DB_VERSION_1 = "1"; + /** * List of components and apps that can have running {@link ManagedServices}. @@ -107,7 +110,7 @@ abstract public class ManagedServices { static final String ATT_VERSION = "version"; static final String ATT_DEFAULTS = "defaults"; - static final int DB_VERSION = 1; + static final int DB_VERSION = 2; static final int APPROVAL_BY_PACKAGE = 0; static final int APPROVAL_BY_COMPONENT = 1; @@ -187,17 +190,22 @@ abstract public class ManagedServices { protected void addDefaultComponentOrPackage(String packageOrComponent) { if (!TextUtils.isEmpty(packageOrComponent)) { synchronized (mDefaultsLock) { - ComponentName cn = ComponentName.unflattenFromString(packageOrComponent); - if (cn == null) { + if (mApprovalLevel == APPROVAL_BY_PACKAGE) { mDefaultPackages.add(packageOrComponent); - } else { + return; + } + ComponentName cn = ComponentName.unflattenFromString(packageOrComponent); + if (cn != null && mApprovalLevel == APPROVAL_BY_COMPONENT) { mDefaultPackages.add(cn.getPackageName()); mDefaultComponents.add(cn); + return; } } } } + protected abstract void loadDefaultsFromConfig(); + boolean isDefaultComponentOrPackage(String packageOrComponent) { synchronized (mDefaultsLock) { ComponentName cn = ComponentName.unflattenFromString(packageOrComponent); @@ -504,19 +512,19 @@ abstract public class ManagedServices { void readDefaults(XmlPullParser parser) { String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS); - if (defaultComponents == null) { - return; - } - String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR); - synchronized (mDefaultsLock) { - for (int i = 0; i < components.length; i++) { - if (!TextUtils.isEmpty(components[i])) { - ComponentName cn = ComponentName.unflattenFromString(components[i]); - if (cn != null) { - mDefaultPackages.add(cn.getPackageName()); - mDefaultComponents.add(cn); - } else { - mDefaultPackages.add(components[i]); + + if (!TextUtils.isEmpty(defaultComponents)) { + String[] components = defaultComponents.split(ENABLED_SERVICES_SEPARATOR); + synchronized (mDefaultsLock) { + for (int i = 0; i < components.length; i++) { + if (!TextUtils.isEmpty(components[i])) { + ComponentName cn = ComponentName.unflattenFromString(components[i]); + if (cn != null) { + mDefaultPackages.add(cn.getPackageName()); + mDefaultComponents.add(cn); + } else { + mDefaultPackages.add(components[i]); + } } } } @@ -531,9 +539,11 @@ abstract public class ManagedServices { throws XmlPullParserException, IOException { // read grants int type; + String version = ""; readDefaults(parser); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { String tag = parser.getName(); + version = XmlUtils.readStringAttribute(parser, ATT_VERSION); if (type == XmlPullParser.END_TAG && getConfig().xmlTag.equals(tag)) { break; @@ -561,9 +571,38 @@ abstract public class ManagedServices { } } } + boolean isVersionOne = TextUtils.isEmpty(version) || DB_VERSION_1.equals(version); + if (isVersionOne) { + upgradeToVersionTwo(); + } rebindServices(false, USER_ALL); } + private void upgradeToVersionTwo() { + // check if any defaults are loaded + int defaultsSize = mDefaultComponents.size() + mDefaultPackages.size(); + if (defaultsSize == 0) { + // load defaults from current allowed + if (this.mApprovalLevel == APPROVAL_BY_COMPONENT) { + List<ComponentName> approvedComponents = getAllowedComponents(USER_SYSTEM); + for (int i = 0; i < approvedComponents.size(); i++) { + addDefaultComponentOrPackage(approvedComponents.get(i).flattenToString()); + } + } + if (this.mApprovalLevel == APPROVAL_BY_PACKAGE) { + List<String> approvedPkgs = getAllowedPackages(USER_SYSTEM); + for (int i = 0; i < approvedPkgs.size(); i++) { + addDefaultComponentOrPackage(approvedPkgs.get(i)); + } + } + } + // if no defaults are loaded, then load from config + defaultsSize = mDefaultComponents.size() + mDefaultPackages.size(); + if (defaultsSize == 0) { + loadDefaultsFromConfig(); + } + } + /** * Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag. */ diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 54efe543a29f..9b02b48f7825 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -108,6 +108,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -156,6 +157,7 @@ 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.ShortcutServiceInternal; import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; @@ -220,6 +222,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseArrayMap; import android.util.StatsEvent; import android.util.Xml; import android.util.proto.ProtoOutputStream; @@ -291,6 +294,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.Objects; @@ -528,13 +532,15 @@ public class NotificationManagerService extends SystemService { private NotificationRecordLogger mNotificationRecordLogger; private InstanceIdSequence mNotificationInstanceIdSequence; - private static class Archive { + static class Archive { + final SparseArray<Boolean> mEnabled; final int mBufferSize; - final ArrayDeque<Pair<StatusBarNotification, Integer>> mBuffer; + final LinkedList<Pair<StatusBarNotification, Integer>> mBuffer; public Archive(int size) { mBufferSize = size; - mBuffer = new ArrayDeque<>(mBufferSize); + mBuffer = new LinkedList<>(); + mEnabled = new SparseArray<>(); } public String toString() { @@ -547,7 +553,10 @@ public class NotificationManagerService extends SystemService { return sb.toString(); } - public void record(StatusBarNotification nr, int reason) { + public void record(StatusBarNotification sbn, int reason) { + if (!mEnabled.get(sbn.getNormalizedUserId(), false)) { + return; + } if (mBuffer.size() == mBufferSize) { mBuffer.removeFirst(); } @@ -555,7 +564,7 @@ public class NotificationManagerService extends SystemService { // We don't want to store the heavy bits of the notification in the archive, // but other clients in the system process might be using the object, so we // store a (lightened) copy. - mBuffer.addLast(new Pair<>(nr.cloneLight(), reason)); + mBuffer.addLast(new Pair<>(sbn.cloneLight(), reason)); } public Iterator<Pair<StatusBarNotification, Integer>> descendingIterator() { @@ -577,60 +586,25 @@ public class NotificationManagerService extends SystemService { return a.toArray(new StatusBarNotification[a.size()]); } - } + public void updateHistoryEnabled(@UserIdInt int userId, boolean enabled) { + mEnabled.put(userId, enabled); - void loadDefaultApprovedServices(int userId) { - String defaultListenerAccess = getContext().getResources().getString( - com.android.internal.R.string.config_defaultListenerAccessPackages); - if (defaultListenerAccess != null) { - String[] listeners = - defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR); - for (int i = 0; i < listeners.length; i++) { - if (TextUtils.isEmpty(listeners[i])) { - continue; - } - ArraySet<ComponentName> approvedListeners = - mListeners.queryPackageForServices(listeners[i], - MATCH_DIRECT_BOOT_AWARE - | MATCH_DIRECT_BOOT_UNAWARE, userId); - for (int k = 0; k < approvedListeners.size(); k++) { - ComponentName cn = approvedListeners.valueAt(k); - mListeners.addDefaultComponentOrPackage(cn.flattenToString()); + if (!enabled) { + for (int i = mBuffer.size() - 1; i >= 0; i--) { + if (userId == mBuffer.get(i).first.getNormalizedUserId()) { + mBuffer.remove(i); + } } } } + } - String defaultDndAccess = getContext().getResources().getString( - com.android.internal.R.string.config_defaultDndAccessPackages); - if (defaultDndAccess != null) { - String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR); - for (int i = 0; i < dnds.length; i++) { - if (TextUtils.isEmpty(dnds[i])) { - continue; - } - mConditionProviders.addDefaultComponentOrPackage(dnds[i]); - } - } + void loadDefaultApprovedServices(int userId) { + mListeners.loadDefaultsFromConfig(); + mConditionProviders.loadDefaultsFromConfig(); - ArraySet<String> assistants = new ArraySet<>(); - String deviceAssistant = DeviceConfig.getProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE); - if (deviceAssistant != null) { - assistants.addAll(Arrays.asList(deviceAssistant.split( - ManagedServices.ENABLED_SERVICES_SEPARATOR))); - } - assistants.addAll(Arrays.asList(getContext().getResources().getString( - com.android.internal.R.string.config_defaultAssistantAccessComponent) - .split(ManagedServices.ENABLED_SERVICES_SEPARATOR))); - for (int i = 0; i < assistants.size(); i++) { - String cnString = assistants.valueAt(i); - if (TextUtils.isEmpty(cnString)) { - continue; - } - mAssistants.addDefaultComponentOrPackage(cnString); - } + mAssistants.loadDefaultsFromConfig(); } protected void allowDefaultApprovedServices(int userId) { @@ -653,11 +627,14 @@ public class NotificationManagerService extends SystemService { DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE); if (overrideDefaultAssistantString != null) { - ComponentName overrideDefaultAssistant = - ComponentName.unflattenFromString(overrideDefaultAssistantString); - if (allowAssistant(userId, overrideDefaultAssistant)) return; + ArraySet<ComponentName> approved = mAssistants.queryPackageForServices( + overrideDefaultAssistantString, + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + userId); + for (int i = 0; i < approved.size(); i++) { + if (allowAssistant(userId, approved.valueAt(i))) return; + } } - ArraySet<ComponentName> defaults = mAssistants.getDefaultComponents(); // We should have only one default assistant by default // allowAssistant should execute once in practice @@ -1638,6 +1615,9 @@ public class NotificationManagerService extends SystemService { = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); private final Uri NOTIFICATION_RATE_LIMIT_URI = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE); + private final Uri NOTIFICATION_HISTORY_ENABLED + = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED); + SettingsObserver(Handler handler) { super(handler); @@ -1653,10 +1633,12 @@ public class NotificationManagerService extends SystemService { false, this, UserHandle.USER_ALL); resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI, false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(NOTIFICATION_HISTORY_ENABLED, + false, this, UserHandle.USER_ALL); update(null); } - @Override public void onChange(boolean selfChange, Uri uri) { + @Override public void onChange(boolean selfChange, Uri uri, int userId) { update(uri); } @@ -1681,6 +1663,14 @@ public class NotificationManagerService extends SystemService { if (uri == null || NOTIFICATION_BUBBLES_URI.equals(uri)) { mPreferencesHelper.updateBubblesEnabled(); } + if (uri == null || NOTIFICATION_HISTORY_ENABLED.equals(uri)) { + final IntArray userIds = mUserProfiles.getCurrentProfileIds(); + + for (int i = 0; i < userIds.size(); i++) { + mArchive.updateHistoryEnabled(userIds.get(i), Settings.Secure.getInt(resolver, + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0) == 1); + } + } } } @@ -1959,7 +1949,8 @@ public class NotificationManagerService extends SystemService { mPackageManagerClient, mRankingHandler, mZenModeHelper, - new NotificationChannelLoggerImpl()); + new NotificationChannelLoggerImpl(), + mAppOps); mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper, @@ -2300,7 +2291,8 @@ public class NotificationManagerService extends SystemService { mRoleObserver.init(); LauncherApps launcherApps = (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE); - mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener); + mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener, getLocalService( + ShortcutServiceInternal.class)); BubbleExtractor bubbsExtractor = mRankingHelper.findExtractor(BubbleExtractor.class); if (bubbsExtractor != null) { bubbsExtractor.setShortcutHelper(mShortcutHelper); @@ -8568,6 +8560,26 @@ public class NotificationManagerService extends SystemService { private ArrayMap<Integer, Boolean> mUserSetMap = new ArrayMap<>(); private Set<String> mAllowedAdjustments = new ArraySet<>(); + @Override + protected void loadDefaultsFromConfig() { + ArraySet<String> assistants = new ArraySet<>(); + assistants.addAll(Arrays.asList(mContext.getResources().getString( + com.android.internal.R.string.config_defaultAssistantAccessComponent) + .split(ManagedServices.ENABLED_SERVICES_SEPARATOR))); + for (int i = 0; i < assistants.size(); i++) { + String cnString = assistants.valueAt(i); + if (TextUtils.isEmpty(cnString)) { + continue; + } + ArraySet<ComponentName> approved = queryPackageForServices(cnString, + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM); + for (int k = 0; k < approved.size(); k++) { + ComponentName cn = approved.valueAt(k); + addDefaultComponentOrPackage(cn.flattenToString()); + } + } + } + public NotificationAssistants(Context context, Object lock, UserProfiles up, IPackageManager pm) { super(context, lock, up, pm); @@ -9005,7 +9017,29 @@ public class NotificationManagerService extends SystemService { public NotificationListeners(IPackageManager pm) { super(getContext(), mNotificationLock, mUserProfiles, pm); + } + @Override + protected void loadDefaultsFromConfig() { + String defaultListenerAccess = mContext.getResources().getString( + R.string.config_defaultListenerAccessPackages); + if (defaultListenerAccess != null) { + String[] listeners = + defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR); + for (int i = 0; i < listeners.length; i++) { + if (TextUtils.isEmpty(listeners[i])) { + continue; + } + ArraySet<ComponentName> approvedListeners = + this.queryPackageForServices(listeners[i], + MATCH_DIRECT_BOOT_AWARE + | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM); + for (int k = 0; k < approvedListeners.size(); k++) { + ComponentName cn = approvedListeners.valueAt(k); + addDefaultComponentOrPackage(cn.flattenToString()); + } + } + } } @Override diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 192df4139b37..2bbbffc203f4 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -590,6 +590,8 @@ public final class NotificationRecord { pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria())); } pw.println(prefix + "mAdjustments=" + mAdjustments); + pw.println(prefix + "shortcut=" + notification.getShortcutId() + + " found valid? " + (mShortcutInfo != null)); } @Override diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index b3d373ffab3a..d432fc83b52a 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -16,7 +16,9 @@ package com.android.server.notification; +import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; @@ -30,6 +32,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; @@ -79,7 +82,9 @@ import java.util.concurrent.ConcurrentHashMap; public class PreferencesHelper implements RankingConfig { private static final String TAG = "NotificationPrefHelper"; - private static final int XML_VERSION = 1; + private static final int XML_VERSION = 2; + /** What version to check to do the upgrade for bubbles. */ + private static final int XML_VERSION_BUBBLES_UPGRADE = 1; private static final int UNKNOWN_UID = UserHandle.USER_NULL; private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":"; @@ -151,6 +156,7 @@ public class PreferencesHelper implements RankingConfig { private final RankingHandler mRankingHandler; private final ZenModeHelper mZenModeHelper; private final NotificationChannelLogger mNotificationChannelLogger; + private final AppOpsManager mAppOps; private SparseBooleanArray mBadgingEnabled; private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE; @@ -167,12 +173,14 @@ public class PreferencesHelper implements RankingConfig { } public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, - ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger) { + ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger, + AppOpsManager appOpsManager) { mContext = context; mZenModeHelper = zenHelper; mRankingHandler = rankingHandler; mPm = pm; mNotificationChannelLogger = notificationChannelLogger; + mAppOps = appOpsManager; // STOPSHIP (b/142218092) this should be removed before ship if (!wasBadgingForcedTrue(context)) { @@ -195,6 +203,15 @@ public class PreferencesHelper implements RankingConfig { if (type != XmlPullParser.START_TAG) return; String tag = parser.getName(); if (!TAG_RANKING.equals(tag)) return; + + boolean upgradeForBubbles = false; + if (parser.getAttributeCount() > 0) { + String attribute = parser.getAttributeName(0); + if (ATT_VERSION.equals(attribute)) { + int xmlVersion = Integer.parseInt(parser.getAttributeValue(0)); + upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE; + } + } synchronized (mPackagePreferences) { while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); @@ -220,6 +237,16 @@ public class PreferencesHelper implements RankingConfig { } } boolean skipWarningLogged = false; + boolean hasSAWPermission = false; + if (upgradeForBubbles) { + hasSAWPermission = mAppOps.noteOpNoThrow( + OP_SYSTEM_ALERT_WINDOW, uid, name, null, + "check-notif-bubble") == AppOpsManager.MODE_ALLOWED; + } + int bubblePref = hasSAWPermission + ? BUBBLE_PREFERENCE_ALL + : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE, + DEFAULT_BUBBLE_PREFERENCE); PackagePreferences r = getOrCreatePackagePreferencesLocked( name, userId, uid, @@ -231,8 +258,7 @@ public class PreferencesHelper implements RankingConfig { parser, ATT_VISIBILITY, DEFAULT_VISIBILITY), XmlUtils.readBooleanAttribute( parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), - XmlUtils.readIntAttribute( - parser, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE)); + bubblePref); r.importance = XmlUtils.readIntAttribute( parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); r.priority = XmlUtils.readIntAttribute( diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java index f1ce3a7d9f48..1d4843822931 100644 --- a/services/core/java/com/android/server/notification/ShortcutHelper.java +++ b/services/core/java/com/android/server/notification/ShortcutHelper.java @@ -21,11 +21,15 @@ 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.Intent; +import android.content.IntentFilter; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutServiceInternal; import android.os.Binder; import android.os.Handler; import android.os.UserHandle; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -38,6 +42,7 @@ import java.util.List; * Helper for querying shortcuts. */ class ShortcutHelper { + private static final String TAG = "ShortcutHelper"; /** * Listener to call when a shortcut we're tracking has been removed. @@ -48,6 +53,8 @@ class ShortcutHelper { private LauncherApps mLauncherAppsService; private ShortcutListener mShortcutListener; + private ShortcutServiceInternal mShortcutServiceInternal; + private IntentFilter mSharingFilter; // Key: packageName Value: <shortcutId, notifId> private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>(); @@ -111,9 +118,17 @@ class ShortcutHelper { } }; - ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener) { + ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener, + ShortcutServiceInternal shortcutServiceInternal) { mLauncherAppsService = launcherApps; mShortcutListener = listener; + mSharingFilter = new IntentFilter(); + try { + mSharingFilter.addDataType("*/*"); + } catch (IntentFilter.MalformedMimeTypeException e) { + Slog.e(TAG, "Bad mime type", e); + } + mShortcutServiceInternal = shortcutServiceInternal; } @VisibleForTesting @@ -121,6 +136,11 @@ class ShortcutHelper { mLauncherAppsService = launcherApps; } + @VisibleForTesting + void setShortcutServiceInternal(ShortcutServiceInternal shortcutServiceInternal) { + mShortcutServiceInternal = shortcutServiceInternal; + } + /** * Only returns shortcut info if it's found and if it's {@link ShortcutInfo#isLongLived()}. */ @@ -141,7 +161,14 @@ class ShortcutHelper { ShortcutInfo info = shortcuts != null && shortcuts.size() > 0 ? shortcuts.get(0) : null; - return info != null && info.isLongLived() ? info : null; + if (info == null || !info.isLongLived() || !info.isEnabled()) { + return null; + } + if (mShortcutServiceInternal.isSharingShortcut(user.getIdentifier(), + "android", packageName, shortcutId, user.getIdentifier(), mSharingFilter)) { + return info; + } + return null; } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 9fb468e8db6e..7cee286c4451 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -71,6 +71,8 @@ public class Installer extends SystemService { public static final int DEXOPT_GENERATE_COMPACT_DEX = 1 << 11; /** Indicates that dexopt should generate an app image */ public static final int DEXOPT_GENERATE_APP_IMAGE = 1 << 12; + /** Indicates that dexopt may be run with different performance / priority tuned for restore */ + public static final int DEXOPT_FOR_RESTORE = 1 << 13; // TODO(b/135202722): remove public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE; diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 1951e7417b2c..4b8a24204ca7 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -22,6 +22,7 @@ import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE; import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE; import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS; import static com.android.server.pm.Installer.DEXOPT_FORCE; +import static com.android.server.pm.Installer.DEXOPT_FOR_RESTORE; import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE; import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX; import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB; @@ -706,6 +707,7 @@ public class PackageDexOptimizer { | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0) | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0) | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0) + | (options.isDexoptInstallForRestore() ? DEXOPT_FOR_RESTORE : 0) | hiddenApiFlag; return adjustDexoptFlags(dexFlags); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 59ac603875e2..d2481b758e82 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -66,6 +66,8 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE import static android.content.pm.PackageManager.INSTALL_INTERNAL; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; +import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE; +import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; @@ -94,6 +96,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.RESTRICTION_NONE; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; +import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; import static android.content.pm.PackageParser.isApkFile; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.incremental.IncrementalManager.isIncrementalPath; @@ -1820,10 +1823,12 @@ public class PackageManagerService extends IPackageManager.Stub state.setVerifierResponse(Binder.getCallingUid(), PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); broadcastPackageVerified(verificationId, originUri, - PackageManager.VERIFICATION_ALLOW, user); + PackageManager.VERIFICATION_ALLOW, null, args.mDataLoaderType, + user); } else { broadcastPackageVerified(verificationId, originUri, - PackageManager.VERIFICATION_REJECT, user); + PackageManager.VERIFICATION_REJECT, null, args.mDataLoaderType, + user); params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); state.setVerifierResponse(Binder.getCallingUid(), @@ -1899,7 +1904,7 @@ public class PackageManagerService extends IPackageManager.Stub if (state.isInstallAllowed()) { broadcastPackageVerified(verificationId, originUri, - response.code, args.getUser()); + response.code, null, args.mDataLoaderType, args.getUser()); } else { params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); @@ -3576,7 +3581,8 @@ public class PackageManagerService extends IPackageManager.Stub // Prepare a supplier of package parser for the staging manager to parse apex file // during the staging installation. final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2( - mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback); + mSeparateProcesses, mOnlyCore, mMetrics, null /* cacheDir */, + mPackageParserCallback); mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); @@ -13575,12 +13581,17 @@ public class PackageManagerService extends IPackageManager.Stub } private void broadcastPackageVerified(int verificationId, Uri packageUri, - int verificationCode, UserHandle user) { + int verificationCode, @Nullable String rootHashString, int dataLoaderType, + UserHandle user) { final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED); intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId); intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode); + if (rootHashString != null) { + intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString); + } + intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); mContext.sendBroadcastAsUser(intent, user, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT); @@ -14952,8 +14963,17 @@ public class PackageManagerService extends IPackageManager.Stub verificationState.setRequiredVerifierUid(requiredUid); final int installerUid = verificationInfo == null ? -1 : verificationInfo.installerUid; - if (!origin.existing && isVerificationEnabled(pkgLite, verifierUser.getIdentifier(), - installFlags, installerUid)) { + final boolean isVerificationEnabled = isVerificationEnabled( + pkgLite, verifierUser.getIdentifier(), installFlags, installerUid); + final boolean isV4Signed = + (mArgs.signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4); + final boolean isIncrementalInstall = + (mArgs.mDataLoaderType == DataLoaderType.INCREMENTAL); + // NOTE: We purposefully skip verification for only incremental installs when there's + // a v4 signature block. Otherwise, proceed with verification as usual. + if (!origin.existing + && isVerificationEnabled + && (!isIncrementalInstall || !isV4Signed)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); @@ -16569,7 +16589,29 @@ public class PackageManagerService extends IPackageManager.Stub } executePostCommitSteps(commitRequest); } finally { - if (!success) { + if (success) { + for (InstallRequest request : requests) { + final InstallArgs args = request.args; + if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) { + continue; + } + if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) { + continue; + } + // For incremental installs, we bypass the verifier prior to install. Now + // that we know the package is valid, send a notice to the verifier with + // the root hash of the base.apk. + final String baseCodePath = request.installResult.pkg.getBaseCodePath(); + final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths(); + final Uri originUri = Uri.fromFile(args.origin.resolvedFile); + final int verificationId = mPendingVerificationToken++; + final String rootHashString = PackageManagerServiceUtils + .buildVerificationRootHashString(baseCodePath, splitCodePaths); + broadcastPackageVerified(verificationId, originUri, + PackageManager.VERIFICATION_ALLOW, rootHashString, + args.mDataLoaderType, args.getUser()); + } + } else { for (ScanResult result : preparedScans.values()) { if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(), false)) { @@ -16670,10 +16712,15 @@ public class PackageManagerService extends IPackageManager.Stub // method because `pkg` may not be in `mPackages` yet. // // Also, don't fail application installs if the dexopt step fails. + int flags = DexoptOptions.DEXOPT_BOOT_COMPLETE + | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; + if (reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_RESTORE + || reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_SETUP) { + flags |= DexoptOptions.DEXOPT_FOR_RESTORE; + } DexoptOptions dexoptOptions = new DexoptOptions(packageName, REASON_INSTALL, - DexoptOptions.DEXOPT_BOOT_COMPLETE - | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); + flags); ScanResult result = reconciledPkg.scanResult; // This mirrors logic from commitReconciledScanResultLocked, where the library files @@ -16911,7 +16958,6 @@ public class PackageManagerService extends IPackageManager.Stub if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) { parsedPackage.setSigningDetails(args.signingDetails); } else { - // TODO(b/136132412): skip for Incremental installation parsedPackage.setSigningDetails( ParsingPackageUtils.collectCertificates(parsedPackage, false /* skipVerify */)); } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 91afd846a9c3..5c175a6ef847 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -32,7 +32,6 @@ import android.annotation.Nullable; import android.app.AppGlobals; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -40,7 +39,6 @@ import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.ResolveInfo; import android.content.pm.Signature; -import android.content.pm.parsing.ParsingPackageUtils; import android.os.Build; import android.os.Debug; import android.os.Environment; @@ -50,6 +48,9 @@ import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManagerInternal; +import android.os.incremental.IncrementalManager; +import android.os.incremental.V4Signature; +import android.os.incremental.V4Signature.HashingInfo; import android.service.pm.PackageServiceDumpProto; import android.system.ErrnoException; import android.system.Os; @@ -62,6 +63,7 @@ import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; +import com.android.internal.util.HexDump; import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; @@ -94,8 +96,6 @@ import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.function.Predicate; import java.util.zip.GZIPInputStream; @@ -943,4 +943,71 @@ public class PackageManagerServiceUtils { Os.chmod(currentDir.getAbsolutePath(), mode); } } + + /** + * Returns a string that's compatible with the verification root hash extra. + * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH + */ + @NonNull + public static String buildVerificationRootHashString(@NonNull String baseFilename, + @Nullable String[] splitFilenameArray) { + final StringBuilder sb = new StringBuilder(); + final String baseFilePath = + baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1); + sb.append(baseFilePath).append(":"); + final byte[] baseRootHash = getRootHash(baseFilename); + if (baseRootHash == null) { + sb.append("0"); + } else { + sb.append(HexDump.toHexString(baseRootHash)); + } + if (splitFilenameArray == null || splitFilenameArray.length == 0) { + return sb.toString(); + } + + for (int i = splitFilenameArray.length - 1; i >= 0; i--) { + final String splitFilename = splitFilenameArray[i]; + final String splitFilePath = + splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1); + final byte[] splitRootHash = getRootHash(splitFilename); + sb.append(";").append(splitFilePath).append(":"); + if (splitRootHash == null) { + sb.append("0"); + } else { + sb.append(HexDump.toHexString(splitRootHash)); + } + } + return sb.toString(); + } + + /** + * Returns the root has for the given file. + * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated. + * <p>NOTE: This currently only works on files stored on the incremental file system. The + * eventual goal is that this hash [among others] can be retrieved for any file. + */ + @Nullable + private static byte[] getRootHash(String filename) { + try { + final byte[] baseFileSignature = + IncrementalManager.unsafeGetFileSignature(filename); + if (baseFileSignature == null) { + throw new IOException("File signature not present"); + } + final V4Signature signature = + V4Signature.readFrom(baseFileSignature); + if (signature.hashingInfo == null) { + throw new IOException("Hashing info not present"); + } + final HashingInfo hashInfo = + HashingInfo.fromByteArray(signature.hashingInfo); + if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) { + throw new IOException("Root has not present"); + } + return hashInfo.rawRootHash; + } catch (IOException ignore) { + Slog.e(TAG, "ERROR: could not load root hash from incremental install"); + } + return null; + } } diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java index b453c898ded8..68f38861d7e4 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java +++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java @@ -61,6 +61,10 @@ public final class DexoptOptions { // should get the dex metdata file if present. public static final int DEXOPT_INSTALL_WITH_DEX_METADATA_FILE = 1 << 10; + // When set, indicates that dexopt is being invoked from the install flow during device restore + // or device setup and should be scheduled appropriately. + public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove + // The name of package to optimize. private final String mPackageName; @@ -99,7 +103,8 @@ public final class DexoptOptions { DEXOPT_DOWNGRADE | DEXOPT_AS_SHARED_LIBRARY | DEXOPT_IDLE_BACKGROUND_JOB | - DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; + DEXOPT_INSTALL_WITH_DEX_METADATA_FILE | + DEXOPT_FOR_RESTORE; if ((flags & (~validityMask)) != 0) { throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags)); } @@ -155,6 +160,10 @@ public final class DexoptOptions { return (mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) != 0; } + public boolean isDexoptInstallForRestore() { + return (mFlags & DEXOPT_FOR_RESTORE) != 0; + } + public String getSplitName() { return mSplitName; } diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java index 1c4568095ce3..4bbe3733719e 100644 --- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java +++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java @@ -81,9 +81,6 @@ public class OneTimePermissionUserManager { mAlarmManager = context.getSystemService(AlarmManager.class); mPermissionControllerManager = context.getSystemService(PermissionControllerManager.class); mHandler = context.getMainThreadHandler(); - - // Listen for tracked uid being uninstalled - context.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED)); } /** @@ -171,6 +168,14 @@ public class OneTimePermissionUserManager { } /** + * Register to listen for Uids being uninstalled. This must be done outside of the + * PermissionManagerService lock. + */ + void registerUninstallListener() { + mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED)); + } + + /** * A class which watches a package for inactivity and notifies the permission controller when * the package becomes inactive */ 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 ccc749232dc3..bacc7acf3dc7 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -26,6 +26,7 @@ import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED; 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; +import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; @@ -1656,7 +1657,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int userSettableMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_REVOKED_COMPAT - | FLAG_PERMISSION_REVIEW_REQUIRED; + | FLAG_PERMISSION_REVIEW_REQUIRED + | FLAG_PERMISSION_ONE_TIME; final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED; @@ -3210,16 +3212,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private OneTimePermissionUserManager getOneTimePermissionUserManager(@UserIdInt int userId) { + OneTimePermissionUserManager oneTimePermissionUserManager; synchronized (mLock) { - OneTimePermissionUserManager oneTimePermissionUserManager = + oneTimePermissionUserManager = mOneTimePermissionUserManagers.get(userId); - if (oneTimePermissionUserManager == null) { - oneTimePermissionUserManager = new OneTimePermissionUserManager( - mContext.createContextAsUser(UserHandle.of(userId), /*flags*/ 0)); - mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager); + if (oneTimePermissionUserManager != null) { + return oneTimePermissionUserManager; } - return oneTimePermissionUserManager; + oneTimePermissionUserManager = new OneTimePermissionUserManager( + mContext.createContextAsUser(UserHandle.of(userId), /*flags*/ 0)); + mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager); } + oneTimePermissionUserManager.registerUninstallListener(); + return oneTimePermissionUserManager; } @Override diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java index 67677c6cf17e..e1e6195ad260 100644 --- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java +++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java @@ -41,10 +41,11 @@ public final class ProcfsMemoryUtil { public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) { long[] output = new long[STATUS_KEYS.length]; output[0] = -1; + output[3] = -1; + output[4] = -1; Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output); - if (output[0] == -1 || (output[3] == 0 && output[4] == 0)) { - // Could not open file or anon rss / swap are 0 indicating the process is in a zombie - // state. + if (output[0] == -1 || output[3] == -1 || output[4] == -1) { + // Could not open or parse file. return null; } final MemorySnapshot snapshot = new MemorySnapshot(); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e79b804f76f9..521ffa50f869 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1374,8 +1374,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Rect spaceToFill = transformedBounds != null ? transformedBounds : inMultiWindowMode() - ? task.getDisplayedBounds() - : getRootTask().getParent().getDisplayedBounds(); + ? task.getBounds() + : getRootTask().getParent().getBounds(); mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint); } else if (mLetterbox != null) { mLetterbox.hide(); @@ -6663,17 +6663,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return super.getBounds(); } - @Override - Rect getDisplayedBounds() { - if (task != null) { - final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds(); - if (!overrideDisplayedBounds.isEmpty()) { - return overrideDisplayedBounds; - } - } - return getBounds(); - } - @VisibleForTesting @Override Rect getAnimationBounds(int appStackClipMode) { diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 8bf46bc7c2e8..5968eede0a27 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -91,7 +91,6 @@ import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS; import static com.android.server.wm.TaskProto.BOUNDS; import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER; import static com.android.server.wm.TaskProto.DEFER_REMOVAL; -import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS; import static com.android.server.wm.TaskProto.DISPLAY_ID; import static com.android.server.wm.TaskProto.FILLS_PARENT; import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS; @@ -660,8 +659,7 @@ class ActivityStack extends Task { setBounds(newBounds); } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) { // For pinned stack, resize is now part of the {@link WindowContainerTransaction} - resize(new Rect(newBounds), null /* configBounds */, - PRESERVE_WINDOWS, true /* deferResume */); + resize(new Rect(newBounds), PRESERVE_WINDOWS, true /* deferResume */); } } if (prevIsAlwaysOnTop != isAlwaysOnTop()) { @@ -835,8 +833,7 @@ class ActivityStack extends Task { } if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) { - resize(mTmpRect2, null /*configBounds*/, - false /*preserveWindows*/, true /*deferResume*/); + resize(mTmpRect2, false /*preserveWindows*/, true /*deferResume*/); } } finally { mAtmService.continueWindowLayout(); @@ -894,9 +891,6 @@ class ActivityStack extends Task { setTaskBounds(mDeferredBounds); setBounds(mDeferredBounds); } - if (mUpdateDisplayedBoundsDeferredCalled) { - setTaskDisplayedBounds(mDeferredDisplayedBounds); - } } } @@ -2966,8 +2960,7 @@ class ActivityStack extends Task { // TODO: Can only be called from special methods in ActivityStackSupervisor. // Need to consolidate those calls points into this resize method so anyone can call directly. - void resize(Rect displayedBounds, Rect configBounds, boolean preserveWindows, - boolean deferResume) { + void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) { if (!updateBoundsAllowed(displayedBounds)) { return; } @@ -2979,7 +2972,7 @@ class ActivityStack extends Task { // Update override configurations of all tasks in the stack. final PooledConsumer c = PooledLambda.obtainConsumer( ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class), - displayedBounds, configBounds); + displayedBounds); forAllTasks(c, true /* traverseTopToBottom */); c.recycle(); @@ -3000,17 +2993,10 @@ class ActivityStack extends Task { } } - private static void processTaskResizeBounds( - Task task, Rect displayedBounds, Rect configBounds) { + private static void processTaskResizeBounds(Task task, Rect displayedBounds) { if (!task.isResizeable()) return; - if (configBounds != null && !configBounds.isEmpty()) { - task.setOverrideDisplayedBounds(displayedBounds); - task.setBounds(configBounds); - } else { - task.setOverrideDisplayedBounds(null); - task.setBounds(displayedBounds); - } + task.setBounds(displayedBounds); } /** @@ -3032,22 +3018,6 @@ class ActivityStack extends Task { task.setBounds(task.isResizeable() ? bounds : null); } - /** Helper to setDisplayedBounds on all child tasks */ - private void setTaskDisplayedBounds(Rect bounds) { - if (!updateDisplayedBoundsAllowed(bounds)) { - return; - } - - final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskDisplayedBounds, - PooledLambda.__(Task.class), bounds); - forAllLeafTasks(c, true /* traverseTopToBottom */); - c.recycle(); - } - - private static void setTaskDisplayedBounds(Task task, Rect bounds) { - task.setOverrideDisplayedBounds(bounds == null || bounds.isEmpty() ? null : bounds); - } - /** * Returns the top-most activity that occludes the given one, or @{code null} if none. */ @@ -3569,8 +3539,8 @@ class ActivityStack extends Task { } @Override - void getRelativeDisplayedPosition(Point outPos) { - super.getRelativeDisplayedPosition(outPos); + void getRelativePosition(Point outPos) { + super.getRelativePosition(outPos); final int outset = getStackOutset(); outPos.x -= outset; outPos.y -= outset; @@ -3581,7 +3551,7 @@ class ActivityStack extends Task { return; } - final Rect stackBounds = getDisplayedBounds(); + final Rect stackBounds = getBounds(); int width = stackBounds.width(); int height = stackBounds.height(); @@ -3776,7 +3746,6 @@ class ActivityStack extends Task { proto.write(FILLS_PARENT, matchParentBounds()); getRawBounds().dumpDebug(proto, BOUNDS); - getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS); if (mLastNonFullscreenBounds != null) { mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index f924bd4ea194..3bcccedb6de5 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1357,7 +1357,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // still need moveTaskToFrontLocked() below for any transition settings. } if (stack.shouldResizeStackWithLaunchBounds()) { - stack.resize(bounds, null /* configBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME); + stack.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME); } else { // WM resizeTask must be done after the task is moved to the correct stack, // because Task's setBounds() also updates dim layer's bounds, but that has diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 89985e85452a..a47cdc66fbd8 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -26,6 +26,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; @@ -141,6 +142,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.WindowConfiguration; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.ScreenOrientation; @@ -3370,34 +3372,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + private boolean isImeControlledByApp() { + return mInputMethodTarget != null && !WindowConfiguration.isSplitScreenWindowingMode( + mInputMethodTarget.getWindowingMode()); + } + boolean isImeAttachedToApp() { - return (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null + return isImeControlledByApp() + && mInputMethodTarget.mActivityRecord != null && mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN // An activity with override bounds should be letterboxed inside its parent bounds, // so it doesn't fill the screen. - && mInputMethodTarget.mActivityRecord.matchParentBounds()); - } - - /** - * Get IME target that should host IME when this display that is reparented to another - * WindowState. - * IME is never displayed in a child display. - * Use {@link WindowState#getImeControlTarget()} when IME target window - * which originally called - * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} is known. - * - * @return {@link WindowState} of host that controls IME. - * {@code null} when {@param dc} is not a virtual display. - * @see DisplayContent#reparent - */ - @Nullable - WindowState getImeControlTarget() { - WindowState imeTarget = mInputMethodTarget; - if (imeTarget != null) { - return imeTarget.getImeControlTarget(); - } - - return getInsetsStateController().getImeSourceProvider().getControlTarget().getWindow(); + && mInputMethodTarget.mActivityRecord.matchParentBounds(); } /** @@ -3407,7 +3393,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * * @param target current IME target. * @return {@link WindowState} that can host IME. - * @see DisplayContent#getImeControlTarget() */ WindowState getImeHostOrFallback(WindowState target) { if (target != null && target.getDisplayContent().canShowIme()) { @@ -3448,8 +3433,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * The IME input target is the window which receives input from IME. It is also a candidate * which controls the visibility and animation of the input method window. - * - * @param target the window that receives input from IME. */ void setInputMethodInputTarget(WindowState target) { if (mInputMethodInputTarget != target) { @@ -3459,12 +3442,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private void updateImeControlTarget() { - if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) { - mInputMethodControlTarget = mRemoteInsetsControlTarget; - } else { - // Otherwise, we just use the ime input target - mInputMethodControlTarget = mInputMethodInputTarget; - } + mInputMethodControlTarget = computeImeControlTarget(); mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget); } @@ -3477,6 +3455,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } /** + * Computes the window where we hand IME control to. + */ + @VisibleForTesting + InsetsControlTarget computeImeControlTarget() { + if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) { + return mRemoteInsetsControlTarget; + } else { + // Otherwise, we just use the ime target as received from IME. + return mInputMethodInputTarget; + } + } + + /** * Computes the window the IME should be attached to. */ @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index ba14d48d38ea..4ac319ddf6ce 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -171,7 +171,7 @@ class InsetsStateController { if (aboveIme) { state = new InsetsState(state); - state.removeSource(ITYPE_IME); + state.setSourceVisible(ITYPE_IME, false); } return state; diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 0a9878dd660b..51053b2e7123 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -165,17 +165,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks, ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity"); - // TODO(multi-display) currently only support recents animation in default display. - final DisplayContent dc = - mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent; - if (!mWindowManager.canStartRecentsAnimation()) { - notifyAnimationCancelBeforeStart(recentsAnimationRunner); - ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, - "Can't start recents animation, nextAppTransition=%s", - dc.mAppTransition.getAppTransition()); - return; - } - // If the activity is associated with the recents stack, then try and get that first ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 54210ae1c0b0..2ce10a74c949 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -898,11 +898,11 @@ public class RecentsAnimationController implements DeathRecipient { TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { mTask = task; mIsRecentTaskInvisible = isRecentTaskInvisible; - mBounds.set(mTask.getDisplayedBounds()); + mBounds.set(mTask.getBounds()); mLocalBounds.set(mBounds); Point tmpPos = new Point(); - mTask.getRelativeDisplayedPosition(tmpPos); + mTask.getRelativePosition(tmpPos); mLocalBounds.offsetTo(tmpPos.x, tmpPos.y); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ad1a205a4910..1ffa01c2bd66 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -52,7 +52,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.content.res.Configuration.EMPTY; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -353,10 +352,6 @@ class Task extends WindowContainer<WindowContainer> { final Rect mPreparedFrozenBounds = new Rect(); final Configuration mPreparedFrozenMergedConfig = new Configuration(); - // If non-empty, bounds used to display the task during animations/interactions. - // TODO(b/119687367): This member is temporary. - private final Rect mOverrideDisplayedBounds = new Rect(); - // Id of the previous display the stack was on. int mPrevDisplayId = INVALID_DISPLAY; @@ -2795,29 +2790,6 @@ class Task extends WindowContainer<WindowContainer> { } } - /** - * Displayed bounds are used to set where the task is drawn at any given time. This is - * separate from its actual bounds so that the app doesn't see any meaningful configuration - * changes during transitionary periods. - */ - void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) { - if (overrideDisplayedBounds != null) { - adjustForMinimalTaskDimensions(overrideDisplayedBounds, mOverrideDisplayedBounds); - mOverrideDisplayedBounds.set(overrideDisplayedBounds); - } else { - mOverrideDisplayedBounds.setEmpty(); - } - updateSurfacePosition(); - } - - /** - * Gets the bounds that override where the task is displayed. See - * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed. - */ - Rect getOverrideDisplayedBounds() { - return mOverrideDisplayedBounds; - } - boolean isResizeable(boolean checkSupportsPip) { return (mAtmService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode) || (checkSupportsPip && mSupportsPictureInPicture)); @@ -2851,49 +2823,6 @@ class Task extends WindowContainer<WindowContainer> { mPreparedFrozenMergedConfig.setTo(getConfiguration()); } - /** - * Align the task to the adjusted bounds. - * - * @param adjustedBounds Adjusted bounds to which the task should be aligned. - * @param tempInsetBounds Insets bounds for the task. - * @param alignBottom True if the task's bottom should be aligned to the adjusted - * bounds's bottom; false if the task's top should be aligned - * the adjusted bounds's top. - */ - void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) { - if (!isResizeable() || EMPTY.equals(getRequestedOverrideConfiguration())) { - return; - } - - getBounds(mTmpRect2); - if (alignBottom) { - int offsetY = adjustedBounds.bottom - mTmpRect2.bottom; - mTmpRect2.offset(0, offsetY); - } else { - mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top); - } - if (tempInsetBounds == null || tempInsetBounds.isEmpty()) { - setOverrideDisplayedBounds(null); - setBounds(mTmpRect2); - } else { - setOverrideDisplayedBounds(mTmpRect2); - setBounds(tempInsetBounds); - } - } - - /** - * Gets the current overridden displayed bounds. These will be empty if the task is not - * currently overriding where it is displayed. - */ - @Override - public Rect getDisplayedBounds() { - if (mOverrideDisplayedBounds.isEmpty()) { - return super.getDisplayedBounds(); - } else { - return mOverrideDisplayedBounds; - } - } - @Override void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets, Rect outSurfaceInsets) { @@ -3431,7 +3360,6 @@ class Task extends WindowContainer<WindowContainer> { pw.println(prefix + "taskId=" + mTaskId); pw.println(doublePrefix + "mBounds=" + getBounds().toShortString()); pw.println(doublePrefix + "appTokens=" + mChildren); - pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString()); final String triplePrefix = doublePrefix + " "; final String quadruplePrefix = triplePrefix + " "; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 77530fb629bc..899ab247077a 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2062,7 +2062,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // TODO: Remove this and use #getBounds() instead once we set an app transition animation // on TaskStack. Rect getAnimationBounds(int appStackClipMode) { - return getDisplayedBounds(); + return getBounds(); } /** @@ -2124,7 +2124,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // Separate position and size for use in animators. mTmpRect.set(getAnimationBounds(appStackClipMode)); if (sHierarchicalAnimations) { - getRelativeDisplayedPosition(mTmpPoint); + getRelativePosition(mTmpPoint); } else { mTmpPoint.set(mTmpRect.left, mTmpRect.top); } @@ -2399,7 +2399,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return; } - getRelativeDisplayedPosition(mTmpPos); + getRelativePosition(mTmpPos); if (mTmpPos.equals(mLastSurfacePosition)) { return; } @@ -2414,16 +2414,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** - * Displayed bounds specify where to display this container at. It differs from bounds during - * certain operations (like animation or interactive dragging). - * - * @return the bounds to display this container at. - */ - Rect getDisplayedBounds() { - return getBounds(); - } - - /** * The {@code outFrame} retrieved by this method specifies where the animation will finish * the entrance animation, as the next frame will display the window at these coordinates. In * case of exit animation, this is where the animation will start, as the frame before the @@ -2443,7 +2433,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< outSurfaceInsets.setEmpty(); } - void getRelativeDisplayedPosition(Point outPos) { + void getRelativePosition(Point outPos) { // In addition to updateSurfacePosition, we keep other code that sets // position from fighting with the organizer if (isOrganized()) { @@ -2451,11 +2441,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return; } - final Rect dispBounds = getDisplayedBounds(); + final Rect dispBounds = getBounds(); outPos.set(dispBounds.left, dispBounds.top); final WindowContainer parent = getParent(); if (parent != null) { - final Rect parentBounds = parent.getDisplayedBounds(); + final Rect parentBounds = parent.getBounds(); outPos.offset(-parentBounds.left, -parentBounds.top); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a488af7cdee0..f55a1b3f6ab3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2445,8 +2445,17 @@ public class WindowManagerService extends IWindowManager.Stub if (controls != null) { final int length = Math.min(controls.length, outControls.length); for (int i = 0; i < length; i++) { - outControls[i] = win.isClientLocal() - ? new InsetsSourceControl(controls[i]) : controls[i]; + final InsetsSourceControl control = controls[i]; + + // Check if we are sending invalid leashes. + final SurfaceControl leash = control != null ? control.getLeash() : null; + if (leash != null && !leash.isValid()) { + Slog.wtf(TAG, leash + " is not valid before sending to " + win, + leash.getReleaseStack()); + } + + outControls[i] = win.isClientLocal() && control != null + ? new InsetsSourceControl(control) : control; } } } @@ -2788,18 +2797,6 @@ public class WindowManagerService extends IWindowManager.Stub return mRecentsAnimationController; } - /** - * @return Whether the next recents animation can continue to start. Called from - * {@link RecentsAnimation#startRecentsActivity}. - */ - boolean canStartRecentsAnimation() { - // TODO(multi-display): currently only default display support recent activity - if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) { - return false; - } - return true; - } - void cancelRecentsAnimation( @RecentsAnimationController.ReorderMode int reorderMode, String reason) { if (mRecentsAnimationController != null) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index d9c0219c4779..8b27667475a9 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -320,7 +320,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final ActivityStack stack = (ActivityStack) container; if (stack.inPinnedWindowingMode()) { stack.resize(config.windowConfiguration.getBounds(), - null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */); + PRESERVE_WINDOWS, true /* deferResume */); } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 5f2e14f86c94..af680701a33e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -999,18 +999,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP frame.inset(left, top, right, bottom); } - @Override - public Rect getDisplayedBounds() { - final Task task = getTask(); - if (task != null) { - Rect bounds = task.getOverrideDisplayedBounds(); - if (!bounds.isEmpty()) { - return bounds; - } - } - return super.getDisplayedBounds(); - } - void computeFrame(DisplayFrames displayFrames) { getLayoutingWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); computeFrameLw(); @@ -1065,7 +1053,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP layoutXDiff = 0; layoutYDiff = 0; } else { - windowFrames.mContainingFrame.set(getDisplayedBounds()); + windowFrames.mContainingFrame.set(getBounds()); if (mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) { // If the bounds are frozen, we still want to translate the window freely and only @@ -1223,7 +1211,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left; parentTop = ((WindowState) parent).mWindowFrames.mFrame.top; } else if (parent != null) { - final Rect parentBounds = parent.getDisplayedBounds(); + final Rect parentBounds = parent.getBounds(); parentLeft = parentBounds.left; parentTop = parentBounds.top; } @@ -5290,7 +5278,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x, -parent.mWindowFrames.mFrame.top + mTmpPoint.y); } else if (parentWindowContainer != null) { - final Rect parentBounds = parentWindowContainer.getDisplayedBounds(); + final Rect parentBounds = parentWindowContainer.getBounds(); outPoint.offset(-parentBounds.left, -parentBounds.top); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index d4470f8334ea..99577077d65d 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1030,7 +1030,7 @@ class WindowStateAnimator { mTmpPos.x = 0; mTmpPos.y = 0; if (stack != null) { - stack.getRelativeDisplayedPosition(mTmpPos); + stack.getRelativePosition(mTmpPos); } xOffset = -mTmpPos.x; diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java index 62589ebb92fe..22020ad45666 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java @@ -54,6 +54,7 @@ public class DexoptOptionsTests { assertFalse(opt.isForce()); assertFalse(opt.isDexoptIdleBackgroundJob()); assertFalse(opt.isDexoptInstallWithDexMetadata()); + assertFalse(opt.isDexoptInstallForRestore()); } @Test @@ -67,7 +68,8 @@ public class DexoptOptionsTests { DexoptOptions.DEXOPT_DOWNGRADE | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB | - DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; + DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE | + DexoptOptions.DEXOPT_FOR_RESTORE; DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags); assertEquals(mPackageName, opt.getPackageName()); @@ -82,6 +84,7 @@ public class DexoptOptionsTests { assertTrue(opt.isDexoptAsSharedLibrary()); assertTrue(opt.isDexoptIdleBackgroundJob()); assertTrue(opt.isDexoptInstallWithDexMetadata()); + assertTrue(opt.isDexoptInstallForRestore()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java index af89761acf9d..939b7a0beb49 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java @@ -35,6 +35,7 @@ import android.os.Build; import android.os.Bundle; import android.os.FileUtils; import android.platform.test.annotations.Presubmit; +import android.util.SparseIntArray; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -542,7 +543,12 @@ public class PackageParserLegacyCoreTest { @Test public void testUsesSdk() throws Exception { - parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x); + ParsedPackage pkg = + parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x); + SparseIntArray minExtVers = pkg.getMinExtensionVersions(); + assertEquals(1, minExtVers.size()); + assertEquals(0, minExtVers.get(10000, -1)); + try { parsePackage("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5, x -> x); fail("Expected parsing exception due to incompatible extension SDK version"); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java new file mode 100644 index 000000000000..c69ef8d93282 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java @@ -0,0 +1,143 @@ +/* + * 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.os.UserHandle.USER_CURRENT; +import static android.os.UserHandle.USER_SYSTEM; +import static android.service.notification.NotificationListenerService.REASON_CANCEL; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Notification; +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.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ArchiveTest extends UiServiceTestCase { + private static final int SIZE = 5; + + private NotificationManagerService.Archive mArchive; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mArchive = new NotificationManagerService.Archive(SIZE); + mArchive.updateHistoryEnabled(USER_SYSTEM, true); + mArchive.updateHistoryEnabled(USER_CURRENT, true); + } + + private StatusBarNotification getNotification(String pkg, int id, UserHandle user) { + Notification n = new Notification.Builder(getContext(), "test") + .setContentTitle("A") + .setWhen(1205) + .build(); + return new StatusBarNotification( + pkg, pkg, id, null, 0, 0, n, user, null, System.currentTimeMillis()); + } + + + @Test + public void testRecordAndRead() { + List<String> expected = new ArrayList<>(); + for (int i = 0; i < SIZE; i++) { + StatusBarNotification sbn = getNotification("pkg" + i, i, + UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT)); + expected.add(sbn.getKey()); + mArchive.record(sbn, REASON_CANCEL); + } + + List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true)); + assertThat(actual).hasSize(expected.size()); + for (StatusBarNotification sbn : actual) { + assertThat(expected).contains(sbn.getKey()); + } + } + + @Test + public void testRecordAndRead_overLimit() { + List<String> expected = new ArrayList<>(); + for (int i = 0; i < (SIZE * 2); i++) { + StatusBarNotification sbn = getNotification("pkg" + i, i, UserHandle.of(USER_SYSTEM)); + mArchive.record(sbn, REASON_CANCEL); + if (i >= SIZE) { + expected.add(sbn.getKey()); + } + } + + List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray((SIZE * 2), true)); + assertThat(actual).hasSize(expected.size()); + for (StatusBarNotification sbn : actual) { + assertThat(expected).contains(sbn.getKey()); + } + } + + @Test + public void testDoesNotRecordIfHistoryDisabled() { + mArchive.updateHistoryEnabled(USER_CURRENT, false); + List<String> expected = new ArrayList<>(); + for (int i = 0; i < SIZE; i++) { + StatusBarNotification sbn = getNotification("pkg" + i, i, + UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT)); + mArchive.record(sbn, REASON_CANCEL); + if (i % 2 ==0) { + expected.add(sbn.getKey()); + } + } + + List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true)); + assertThat(actual).hasSize(expected.size()); + for (StatusBarNotification sbn : actual) { + assertThat(expected).contains(sbn.getKey()); + } + } + + @Test + public void testRemovesEntriesWhenHistoryDisabled() { + mArchive.updateHistoryEnabled(USER_CURRENT, true); + List<String> expected = new ArrayList<>(); + for (int i = 0; i < SIZE; i++) { + StatusBarNotification sbn = getNotification("pkg" + i, i, + UserHandle.of(i % 2 ==0 ? USER_SYSTEM : USER_CURRENT)); + mArchive.record(sbn, REASON_CANCEL); + if (i % 2 ==0) { + expected.add(sbn.getKey()); + } + } + mArchive.updateHistoryEnabled(USER_CURRENT, false); + + List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true)); + assertThat(actual).hasSize(expected.size()); + for (StatusBarNotification sbn : actual) { + assertThat(expected).contains(sbn.getKey()); + } + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 7b7470cca85a..28ff9a545513 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -77,6 +77,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -96,6 +97,10 @@ public class ManagedServicesTest extends UiServiceTestCase { UserInfo mZero = new UserInfo(0, "zero", 0); UserInfo mTen = new UserInfo(10, "ten", 0); + private String mDefaultsString; + private String mVersionString; + private final Set<ComponentName> mDefaults = new ArraySet(); + private ManagedServices mService; private static final String SETTING = "setting"; private static final String SECONDARY_SETTING = "secondary_setting"; @@ -106,8 +111,8 @@ public class ManagedServicesTest extends UiServiceTestCase { private ArrayMap<Integer, String> mExpectedSecondaryComponentNames; // type : user : list of approved - private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary = new ArrayMap<>(); - private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary = new ArrayMap<>(); + private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary; + private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary; @Before public void setUp() throws Exception { @@ -132,6 +137,9 @@ public class ManagedServicesTest extends UiServiceTestCase { profileIds.add(12); when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); + mVersionString = "2"; + mExpectedPrimary = new ArrayMap<>(); + mExpectedSecondary = new ArrayMap<>(); mExpectedPrimaryPackages = new ArrayMap<>(); mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package"); mExpectedPrimaryPackages.put(10, "this.is.another.package"); @@ -155,6 +163,8 @@ public class ManagedServicesTest extends UiServiceTestCase { "this.is.another.package:component:package"); mExpectedSecondary.put(APPROVAL_BY_PACKAGE, mExpectedSecondaryPackages); mExpectedSecondary.put(APPROVAL_BY_COMPONENT, mExpectedSecondaryComponentNames); + mService = new TestManagedServices(getContext(), mLock, mUserProfiles, + mIpm, APPROVAL_BY_COMPONENT); } @Test @@ -1178,9 +1188,99 @@ public class ManagedServicesTest extends UiServiceTestCase { } } + @Test + public void loadDefaults_noVersionNoDefaults() throws Exception { + resetComponentsAndPackages(); + loadXml(mService); + assertEquals(mService.getDefaultComponents().size(), 0); + } + + @Test + public void loadDefaults_noVersionNoDefaultsOneActive() throws Exception { + resetComponentsAndPackages(); + mService.addDefaultComponentOrPackage("package/class"); + loadXml(mService); + assertEquals(1, mService.getDefaultComponents().size()); + assertTrue(mService.getDefaultComponents() + .contains(ComponentName.unflattenFromString("package/class"))); + } + + @Test + public void loadDefaults_noVersionWithDefaults() throws Exception { + resetComponentsAndPackages(); + mDefaults.add(new ComponentName("default", "class")); + loadXml(mService); + assertEquals(mService.getDefaultComponents(), mDefaults); + } + + @Test + public void loadDefaults_versionOneWithDefaultsWithActive() throws Exception { + resetComponentsAndPackages(); + mDefaults.add(new ComponentName("default", "class")); + mExpectedPrimaryComponentNames.put(0, "package/class"); + mVersionString = "1"; + loadXml(mService); + assertEquals(mService.getDefaultComponents(), + new ArraySet(Arrays.asList(new ComponentName("package", "class")))); + } + + @Test + public void loadDefaults_versionTwoWithDefaultsWithActive() throws Exception { + resetComponentsAndPackages(); + mDefaults.add(new ComponentName("default", "class")); + mDefaultsString = "default/class"; + mExpectedPrimaryComponentNames.put(0, "package/class"); + mVersionString = "2"; + loadXml(mService); + assertEquals(1, mService.getDefaultComponents().size()); + mDefaults.forEach(pkg -> { + assertTrue(mService.getDefaultComponents().contains(pkg)); + }); + } + + @Test + public void loadDefaults_versionOneWithXMLDefaultsWithActive() throws Exception { + resetComponentsAndPackages(); + mDefaults.add(new ComponentName("default", "class")); + mDefaultsString = "xml/class"; + mExpectedPrimaryComponentNames.put(0, "package/class"); + mVersionString = "1"; + loadXml(mService); + assertEquals(mService.getDefaultComponents(), + new ArraySet(Arrays.asList(new ComponentName("xml", "class")))); + } + + @Test + public void loadDefaults_versionTwoWithXMLDefaultsWithActive() throws Exception { + resetComponentsAndPackages(); + mDefaults.add(new ComponentName("default", "class")); + mDefaultsString = "xml/class"; + mExpectedPrimaryComponentNames.put(0, "package/class"); + mVersionString = "2"; + loadXml(mService); + assertEquals(mService.getDefaultComponents(), + new ArraySet(Arrays.asList(new ComponentName("xml", "class")))); + } + + private void resetComponentsAndPackages() { + ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1); + ArrayMap<Integer, String> emptyPkgs = new ArrayMap(0); + empty.append(mService.mApprovalLevel, emptyPkgs); + mExpectedPrimary = empty; + mExpectedPrimaryComponentNames = emptyPkgs; + mExpectedPrimaryPackages = emptyPkgs; + mExpectedSecondary = empty; + mExpectedSecondaryComponentNames = emptyPkgs; + mExpectedSecondaryPackages = emptyPkgs; + } + private void loadXml(ManagedServices service) throws Exception { final StringBuffer xml = new StringBuffer(); - xml.append("<" + service.getConfig().xmlTag + ">\n"); + String xmlTag = service.getConfig().xmlTag; + xml.append("<" + xmlTag + + (mDefaultsString != null ? " defaults=\"" + mDefaultsString + "\" " : "") + + (mVersionString != null ? " version=\"" + mVersionString + "\" " : "") + + ">\n"); for (int userId : mExpectedPrimary.get(service.mApprovalLevel).keySet()) { xml.append(getXmlEntry( mExpectedPrimary.get(service.mApprovalLevel).get(userId), userId, true)); @@ -1197,7 +1297,7 @@ public class ManagedServicesTest extends UiServiceTestCase { + ManagedServices.ATT_USER_ID + "=\"98\" " + ManagedServices.ATT_IS_PRIMARY + "=\"false\" " + ManagedServices.ATT_APPROVED_LIST + "=\"98\" />\n"); - xml.append("</" + service.getConfig().xmlTag + ">"); + xml.append("</" + xmlTag + ">"); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( @@ -1224,6 +1324,7 @@ public class ManagedServicesTest extends UiServiceTestCase { private void addExpectedServices(final ManagedServices service, final List<String> packages, int userId) { + ManagedServices.Config config = service.getConfig(); when(mPm.queryIntentServicesAsUser(any(), anyInt(), eq(userId))). thenAnswer(new Answer<List<ResolveInfo>>() { @Override @@ -1233,7 +1334,7 @@ public class ManagedServicesTest extends UiServiceTestCase { Intent invocationIntent = (Intent) args[0]; if (invocationIntent != null) { if (invocationIntent.getAction().equals( - service.getConfig().serviceInterface) + config.serviceInterface) && packages.contains(invocationIntent.getPackage())) { List<ResolveInfo> dummyServices = new ArrayList<>(); for (int i = 1; i <= 3; i ++) { @@ -1431,6 +1532,11 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Override + protected void loadDefaultsFromConfig() { + mDefaultComponents.addAll(mDefaults); + } + + @Override protected String getRequiredPermission() { return null; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index 88186cdb3873..ab4dc476ff20 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -72,13 +72,13 @@ public class NotificationAssistantsTest extends UiServiceTestCase { Object mLock = new Object(); + UserInfo mZero = new UserInfo(0, "zero", 0); UserInfo mTen = new UserInfo(10, "ten", 0); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - getContext().setMockPackageManager(mPm); getContext().addMockSystemService(Context.USER_SERVICE, mUm); mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm)); @@ -122,7 +122,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { @Test public void testXmlUpgradeExistingApprovedComponents() throws Exception { - String xml = "<enabled_assistants>" + String xml = "<enabled_assistants version=\"2\" defaults=\"b\\b\">" + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />" + "</enabled_assistants>"; 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 3cd0e92964ec..ecdd9e548e6a 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -113,6 +113,7 @@ import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutServiceInternal; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Color; @@ -234,6 +235,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock private LauncherApps mLauncherApps; @Mock + private ShortcutServiceInternal mShortcutServiceInternal; + @Mock ActivityManager mActivityManager; @Mock Resources mResources; @@ -466,6 +469,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mShortcutHelper = mService.getShortcutHelper(); mShortcutHelper.setLauncherApps(mLauncherApps); + mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); // Set the testable bubble extractor RankingHelper rankingHelper = mService.getRankingHelper(); @@ -782,33 +786,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { userInfos.add(new UserInfo(0, "", 0)); final ArraySet<ComponentName> validAssistants = new ArraySet<>(); validAssistants.add(ComponentName.unflattenFromString(testComponent)); - final String originalComponent = DeviceConfig.getProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE - ); - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE, - testComponent, - false - ); when(mActivityManager.isLowRamDevice()).thenReturn(false); when(mAssistants.queryPackageForServices(isNull(), anyInt(), anyInt())) .thenReturn(validAssistants); - when(mAssistants.getDefaultComponents()).thenReturn(new ArraySet<>()); + when(mAssistants.getDefaultComponents()).thenReturn(validAssistants); when(mUm.getEnabledProfiles(anyInt())).thenReturn(userInfos); mService.setDefaultAssistantForUser(userId); verify(mAssistants).setPackageOrComponentEnabled( eq(testComponent), eq(userId), eq(true), eq(true)); - - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE, - originalComponent, - false - ); } @Test @@ -6088,8 +6075,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { List<ShortcutInfo> shortcutInfos = new ArrayList<>(); ShortcutInfo info = mock(ShortcutInfo.class); when(info.isLongLived()).thenReturn(true); + when(info.isEnabled()).thenReturn(true); shortcutInfos.add(info); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); // Test: Send the bubble notification mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), @@ -6148,8 +6138,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { List<ShortcutInfo> shortcutInfos = new ArrayList<>(); ShortcutInfo info = mock(ShortcutInfo.class); when(info.isLongLived()).thenReturn(true); + when(info.isEnabled()).thenReturn(true); shortcutInfos.add(info); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); // Test: Send the bubble notification mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), @@ -6289,7 +6282,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.loadDefaultApprovedServices(USER_SYSTEM); - verify(mConditionProviders, times(1)).addDefaultComponentOrPackage("test"); + verify(mConditionProviders, times(1)).loadDefaultsFromConfig(); } // TODO: add tests for the rest of the non-empty cases @@ -6492,7 +6485,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ShortcutInfo si = mock(ShortcutInfo.class); when(si.getShortLabel()).thenReturn("Hello"); when(si.isLongLived()).thenReturn(true); + when(si.isEnabled()).thenReturn(true); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si)); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); List<ConversationChannelWrapper> conversations = mBinderService.getConversationsForPackage(PKG_P, mUid).getList(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 427237c4be0f..ac51750f23f8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -15,6 +15,9 @@ */ package com.android.server.notification; +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_DEFAULT; +import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; @@ -50,6 +53,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; @@ -132,6 +136,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Spy IContentProvider mTestIContentProvider = new MockIContentProvider(); @Mock Context mContext; @Mock ZenModeHelper mMockZenModeHelper; + @Mock AppOpsManager mAppOpsManager; private NotificationManager.Policy mTestNotificationPolicy; @@ -187,7 +192,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), + anyString(), eq(null), anyString())).thenReturn(MODE_DEFAULT); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); resetZenModeHelper(); mAudioAttributes = new AudioAttributes.Builder() @@ -1464,7 +1472,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); resetZenModeHelper(); @@ -1475,7 +1484,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { // start notification policy off with mAreChannelsBypassingDnd = false mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); resetZenModeHelper(); @@ -2241,7 +2251,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" + "</package>\n" + "</ranking>\n"; - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadByteArrayXml(preQXml.getBytes(), true, UserHandle.USER_SYSTEM); assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, @@ -2253,7 +2264,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, @@ -2349,7 +2361,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -2360,7 +2373,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -2372,7 +2386,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.revokeNotificationDelegate(PKG_O, UID_O); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -2384,7 +2399,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.toggleNotificationDelegate(PKG_O, UID_O, false); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled @@ -2402,7 +2418,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.revokeNotificationDelegate(PKG_O, UID_O); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled @@ -2417,14 +2434,71 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testBubblePreference_defaults() throws Exception { - assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE); + assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadStreamXml(baos, false, UserHandle.USER_ALL); - assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE); + assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O)); + assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); + } + + @Test + public void testBubblePreference_upgradeWithSAWPermission() throws Exception { + when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(), + anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n" + + "<channel id=\"someId\" name=\"hi\"" + + " importance=\"3\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O)); + assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); + } + + @Test + public void testBubblePreference_upgradeWithSAWThenUserOverride() throws Exception { + when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(), + anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n" + + "<channel id=\"someId\" name=\"hi\"" + + " importance=\"3\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); + + mHelper.setBubblesAllowed(PKG_O, UID_O, BUBBLE_PREFERENCE_SELECTED); + assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, + mHelper.getAppLockedFields(PKG_O, UID_O)); + + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); + loadStreamXml(baos, false, UserHandle.USER_ALL); + + assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, + mHelper.getAppLockedFields(PKG_O, UID_O)); } @Test @@ -2435,7 +2509,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.getAppLockedFields(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE); @@ -2949,7 +3024,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" @@ -2969,7 +3045,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testPlaceholderConversationId_shortcutRequired() throws Exception { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" @@ -2989,7 +3066,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testNormalConversationId_shortcutRequired() throws Exception { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" @@ -3009,7 +3087,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testNoConversationId_shortcutRequired() throws Exception { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, + mAppOpsManager); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java index 50fb9b425652..f7304bd0075b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java @@ -16,7 +16,11 @@ package com.android.server.notification; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -25,6 +29,7 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutServiceInternal; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; @@ -58,6 +63,8 @@ public class ShortcutHelperTest extends UiServiceTestCase { @Mock ShortcutHelper.ShortcutListener mShortcutListener; @Mock + ShortcutServiceInternal mShortcutServiceInternal; + @Mock NotificationRecord mNr; @Mock Notification mNotif; @@ -72,7 +79,8 @@ public class ShortcutHelperTest extends UiServiceTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - mShortcutHelper = new ShortcutHelper(mLauncherApps, mShortcutListener); + mShortcutHelper = new ShortcutHelper( + mLauncherApps, mShortcutListener, mShortcutServiceInternal); when(mNr.getKey()).thenReturn(KEY); when(mNr.getSbn()).thenReturn(mSbn); when(mSbn.getPackageName()).thenReturn(PKG); @@ -138,4 +146,80 @@ public class ShortcutHelperTest extends UiServiceTestCase { callback.onShortcutsChanged(PKG, shortcutInfos, mock(UserHandle.class)); verify(mShortcutListener).onShortcutRemoved(mNr.getKey()); } + + @Test + public void testGetValidShortcutInfo_noMatchingShortcut() { + when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); + + assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull(); + } + + @Test + public void testGetValidShortcutInfo_nullShortcut() { + ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + shortcuts.add(null); + when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); + + assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull(); + } + + @Test + public void testGetValidShortcutInfo_notLongLived() { + ShortcutInfo si = mock(ShortcutInfo.class); + when(si.isLongLived()).thenReturn(false); + when(si.isEnabled()).thenReturn(true); + ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + shortcuts.add(si); + when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); + + assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull(); + } + + @Test + public void testGetValidShortcutInfo_notSharingShortcut() { + ShortcutInfo si = mock(ShortcutInfo.class); + when(si.isLongLived()).thenReturn(true); + when(si.isEnabled()).thenReturn(true); + ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + shortcuts.add(si); + when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(false); + + assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull(); + } + + @Test + public void testGetValidShortcutInfo_notEnabled() { + ShortcutInfo si = mock(ShortcutInfo.class); + when(si.isLongLived()).thenReturn(true); + when(si.isEnabled()).thenReturn(false); + ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + shortcuts.add(si); + when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); + + assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull(); + } + + @Test + public void testGetValidShortcutInfo_isValid() { + ShortcutInfo si = mock(ShortcutInfo.class); + when(si.isLongLived()).thenReturn(true); + when(si.isEnabled()).thenReturn(true); + ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + shortcuts.add(si); + when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); + + assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isSameAs(si); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 4532400e3b34..17dd26ed18e9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -89,13 +89,13 @@ public class ActivityTaskManagerServiceTests extends ActivityTestsBase { public void testOnPictureInPictureRequested() throws RemoteException { final ActivityStack stack = new StackBuilder(mRootWindowContainer).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); - ClientLifecycleManager lifecycleManager = mService.getLifecycleManager(); - doNothing().when(lifecycleManager).scheduleTransaction(any()); + final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class); + doReturn(mockLifecycleManager).when(mService).getLifecycleManager(); doReturn(true).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean()); mService.requestPictureInPictureMode(activity.token); - verify(lifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture()); + verify(mockLifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture()); final ClientTransaction transaction = mClientTransactionCaptor.getValue(); // Check that only an enter pip request item callback was scheduled. assertEquals(1, transaction.getCallbacks().size()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 3fd81b47c546..daff14992e94 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -65,6 +65,7 @@ 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.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; @@ -84,10 +85,13 @@ import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; import android.view.Gravity; +import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowRotationCallback; import android.view.IDisplayWindowRotationController; import android.view.ISystemGestureExclusionListener; import android.view.IWindowManager; +import android.view.InsetsSourceControl; +import android.view.InsetsState; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceControl.Transaction; @@ -810,25 +814,19 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testComputeImeParent_app() throws Exception { - try (final InsetsModeSession session = - new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { - final DisplayContent dc = createNewDisplay(); - dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); - assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(), - dc.computeImeParent()); - } + final DisplayContent dc = createNewDisplay(); + dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); + assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(), + dc.computeImeParent()); } @Test public void testComputeImeParent_app_notFullscreen() throws Exception { - try (final InsetsModeSession session = - new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { - final DisplayContent dc = createNewDisplay(); - dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app"); - dc.mInputMethodTarget.setWindowingMode( - WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent()); - } + final DisplayContent dc = createNewDisplay(); + dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app"); + dc.mInputMethodTarget.setWindowingMode( + WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent()); } @Test @@ -843,12 +841,61 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testComputeImeParent_noApp() throws Exception { - try (final InsetsModeSession session = - new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) { - final DisplayContent dc = createNewDisplay(); - dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar"); - assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent()); - } + final DisplayContent dc = createNewDisplay(); + dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar"); + assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent()); + } + + @Test + public void testComputeImeControlTarget() throws Exception { + final DisplayContent dc = createNewDisplay(); + dc.setRemoteInsetsController(createDisplayWindowInsetsController()); + dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); + dc.mInputMethodTarget = dc.mInputMethodInputTarget; + assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget()); + } + + @Test + public void testComputeImeControlTarget_splitscreen() throws Exception { + final DisplayContent dc = createNewDisplay(); + dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app"); + dc.mInputMethodInputTarget.setWindowingMode( + WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + dc.mInputMethodTarget = dc.mInputMethodInputTarget; + dc.setRemoteInsetsController(createDisplayWindowInsetsController()); + assertNotEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget()); + } + + @Test + public void testComputeImeControlTarget_notMatchParentBounds() throws Exception { + spyOn(mAppWindow.mActivityRecord); + doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds(); + mDisplayContent.mInputMethodInputTarget = mAppWindow; + mDisplayContent.mInputMethodTarget = mDisplayContent.mInputMethodInputTarget; + mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController()); + assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget()); + } + + private IDisplayWindowInsetsController createDisplayWindowInsetsController() { + return new IDisplayWindowInsetsController.Stub() { + + @Override + public void insetsChanged(InsetsState insetsState) throws RemoteException { + } + + @Override + public void insetsControlChanged(InsetsState insetsState, + InsetsSourceControl[] insetsSourceControls) throws RemoteException { + } + + @Override + public void showInsets(int i, boolean b) throws RemoteException { + } + + @Override + public void hideInsets(int i, boolean b) throws RemoteException { + } + }; } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 8369eb7a82f0..9f28f45a05d0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -29,9 +29,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.spy; @@ -44,7 +46,6 @@ import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.test.InsetsModeSession; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.AfterClass; @@ -153,22 +154,24 @@ public class InsetsStateControllerTest extends WindowTestsBase { @Test public void testStripForDispatch_belowIme() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); + getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); - getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + app.mBehindIme = true; - assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME)); + getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); + assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible()); } @Test public void testStripForDispatch_aboveIme() { - final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); - getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + app.mBehindIme = false; - assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME)); + getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); + assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible()); } @Test @@ -190,8 +193,9 @@ public class InsetsStateControllerTest extends WindowTestsBase { mDisplayContent.computeImeTarget(true); mDisplayContent.applySurfaceChangesTransaction(); - // app won't get IME insets while above IME. - assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME)); + // app won't get visible IME insets while above IME even when IME is visible. + getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); + assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible()); // Reset invocation counter. clearInvocations(app); @@ -204,43 +208,44 @@ public class InsetsStateControllerTest extends WindowTestsBase { // Make sure app got notified. verify(app, atLeast(1)).notifyInsetsChanged(); - // app will get IME insets while below IME. - assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME)); + // app will get visible IME insets while below IME when IME is visible. + getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); + assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible()); } @Test public void testStripForDispatch_childWindow_altFocusable() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); final WindowState child = createWindow(app, TYPE_APPLICATION, "child"); child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; - final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); - - // IME cannot be the IME target. - ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE; - - getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + mDisplayContent.computeImeTarget(true); + mDisplayContent.setLayoutNeeded(); + mDisplayContent.applySurfaceChangesTransaction(); - assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME)); + getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); + assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible()); + assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible()); } @Test public void testStripForDispatch_childWindow_splitScreen() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); final WindowState child = createWindow(app, TYPE_APPLICATION, "child"); child.mAttrs.flags |= FLAG_NOT_FOCUSABLE; child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); - - // IME cannot be the IME target. - ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE; - - getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + mDisplayContent.computeImeTarget(true); + mDisplayContent.setLayoutNeeded(); + mDisplayContent.applySurfaceChangesTransaction(); - assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME)); + getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true); + assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible()); + assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 1f6ba7adf114..44ca2cdcb142 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -79,7 +79,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController); doNothing().when(mService.mWindowManager).initializeRecentsAnimation( anyInt(), any(), any(), anyInt(), any(), any()); - doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation(); final RecentTasks recentTasks = mService.getRecentTasks(); spyOn(recentTasks); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index ec77be85aebf..473c1c57d625 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -145,9 +145,5 @@ public class TaskTests extends WindowTestsBase { Rect bounds = new Rect(10, 10, 100, 200); task.setBounds(bounds); assertEquals(new Point(bounds.left, bounds.top), task.getLastSurfacePosition()); - - Rect dispBounds = new Rect(20, 30, 110, 220); - task.setOverrideDisplayedBounds(dispBounds); - assertEquals(new Point(dispBounds.left, dispBounds.top), task.getLastSurfacePosition()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index 7be05a39cbde..eb2aa41192c2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -310,27 +310,6 @@ public class WindowFrameTests extends WindowTestsBase { assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top, resolvedTaskBounds.right - contentInsetRight, resolvedTaskBounds.bottom - contentInsetBottom)); - - pf.set(0, 0, logicalWidth, logicalHeight); - // If we set displayed bounds, the insets will be computed with the main task bounds - // but the frame will be positioned according to the displayed bounds. - final int insetLeft = logicalWidth / 5; - final int insetTop = logicalHeight / 5; - final int insetRight = insetLeft + (resolvedTaskBounds.right - resolvedTaskBounds.left); - final int insetBottom = insetTop + (resolvedTaskBounds.bottom - resolvedTaskBounds.top); - task.setOverrideDisplayedBounds(resolvedTaskBounds); - task.setBounds(insetLeft, insetTop, insetRight, insetBottom); - windowFrames.setFrames(pf, pf, cf, cf, pf, cf); - w.computeFrameLw(); - assertEquals(resolvedTaskBounds, w.getFrameLw()); - assertEquals(0, w.getRelativeFrameLw().left); - assertEquals(0, w.getRelativeFrameLw().top); - contentInsetRight = insetRight - cfRight; - contentInsetBottom = insetBottom - cfBottom; - assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom); - assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top, - resolvedTaskBounds.right - contentInsetRight, - resolvedTaskBounds.bottom - contentInsetBottom)); } @Test @@ -460,33 +439,6 @@ public class WindowFrameTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 130388666) - public void testDisplayCutout_tempDisplayedBounds() { - // Regular fullscreen task and window - WindowState w = createWindow(); - final Task task = w.getTask(); - task.setBounds(new Rect(0, 0, 1000, 2000)); - task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500)); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - - final Rect pf = new Rect(0, -500, 1000, 1500); - // Create a display cutout of size 50x50, aligned top-center - final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP), - pf.width(), pf.height()); - - final WindowFrames windowFrames = w.getWindowFrames(); - windowFrames.setFrames(pf, pf, pf, pf, pf, pf); - windowFrames.setDisplayCutout(cutout); - w.computeFrameLw(); - - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0); - } - - @Test public void testFreeformContentInsets() { removeGlobalMinSizeRestriction(); // fullscreen task doesn't use bounds for computeFrame diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java index c35dd3b783b1..5352be6f283f 100644 --- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java +++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java @@ -397,7 +397,7 @@ public class IorapWorkFlowTest { public LogcatTimestamp() throws Exception{ long currentTimeMillis = System.currentTimeMillis(); epochTime = String.format( - "%d.%d", currentTimeMillis/1000, currentTimeMillis%1000); + "%d.%03d", currentTimeMillis/1000, currentTimeMillis%1000); Log.i(TAG, "Current logcat timestamp is " + epochTime); } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index a116c07e2646..242c2e979571 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -80,7 +80,6 @@ public final class DataCallResponse implements Parcelable { private final int mMtu; private final int mMtuV4; private final int mMtuV6; - private final int mVersion; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -126,9 +125,7 @@ public final class DataCallResponse implements Parcelable { ? new ArrayList<>() : new ArrayList<>(gatewayAddresses); mPcscfAddresses = (pcscfAddresses == null) ? new ArrayList<>() : new ArrayList<>(pcscfAddresses); - mMtu = mtu; - mMtuV4 = mMtuV6 = 0; - mVersion = 0; + mMtu = mMtuV4 = mMtuV6 = mtu; } /** @hide */ @@ -136,7 +133,7 @@ public final class DataCallResponse implements Parcelable { @LinkStatus int linkStatus, @ProtocolType int protocolType, @Nullable String interfaceName, @Nullable List<LinkAddress> addresses, @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, - @Nullable List<InetAddress> pcscfAddresses, int mtuV4, int mtuV6, int version) { + @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -151,10 +148,9 @@ public final class DataCallResponse implements Parcelable { ? new ArrayList<>() : new ArrayList<>(gatewayAddresses); mPcscfAddresses = (pcscfAddresses == null) ? new ArrayList<>() : new ArrayList<>(pcscfAddresses); - mMtu = 0; + mMtu = mtu; mMtuV4 = mtuV4; mMtuV6 = mtuV6; - mVersion = version; } /** @hide */ @@ -177,7 +173,6 @@ public final class DataCallResponse implements Parcelable { mMtu = source.readInt(); mMtuV4 = source.readInt(); mMtuV6 = source.readInt(); - mVersion = source.readInt(); } /** @@ -247,7 +242,7 @@ public final class DataCallResponse implements Parcelable { */ @Deprecated public int getMtu() { - return mVersion < 5 ? mMtu : 0; + return mMtu; } /** @@ -256,7 +251,7 @@ public final class DataCallResponse implements Parcelable { * Zero or negative values means network has either not sent a value or sent an invalid value. */ public int getMtuV4() { - return mVersion < 5 ? 0 : mMtuV4; + return mMtuV4; } /** @@ -264,7 +259,7 @@ public final class DataCallResponse implements Parcelable { * Zero or negative values means network has either not sent a value or sent an invalid value. */ public int getMtuV6() { - return mVersion < 5 ? 0 : mMtuV6; + return mMtuV6; } @NonNull @@ -282,10 +277,9 @@ public final class DataCallResponse implements Parcelable { .append(" dnses=").append(mDnsAddresses) .append(" gateways=").append(mGatewayAddresses) .append(" pcscf=").append(mPcscfAddresses) - .append(" mtu=").append(mMtu) - .append(" mtuV4=").append(mMtuV4) - .append(" mtuV6=").append(mMtuV6) - .append(" version=").append(mVersion) + .append(" mtu=").append(getMtu()) + .append(" mtuV4=").append(getMtuV4()) + .append(" mtuV6=").append(getMtuV6()) .append("}"); return sb.toString(); } @@ -315,15 +309,14 @@ public final class DataCallResponse implements Parcelable { && mPcscfAddresses.containsAll(other.mPcscfAddresses) && mMtu == other.mMtu && mMtuV4 == other.mMtuV4 - && mMtuV6 == other.mMtuV6 - && mVersion == other.mVersion; + && mMtuV6 == other.mMtuV6; } @Override public int hashCode() { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, - mMtu, mMtuV4, mMtuV6, mVersion); + mMtu, mMtuV4, mMtuV6); } @Override @@ -346,7 +339,6 @@ public final class DataCallResponse implements Parcelable { dest.writeInt(mMtu); dest.writeInt(mMtuV4); dest.writeInt(mMtuV6); - dest.writeInt(mVersion); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -403,8 +395,6 @@ public final class DataCallResponse implements Parcelable { private int mMtuV6; - private int mVersion; - /** * Default constructor for Builder. */ @@ -563,29 +553,14 @@ public final class DataCallResponse implements Parcelable { } /** - * Set the IRadio version for this DataCallResponse - * @hide - */ - public @NonNull Builder setVersion(int version) { - mVersion = version; - return this; - } - - /** * Build the DataCallResponse. * * @return the DataCallResponse object. */ public @NonNull DataCallResponse build() { - if (mVersion >= 5) { - return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, - mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, - mPcscfAddresses, mMtuV4, mMtuV6, mVersion); - } else { - return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, - mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, - mPcscfAddresses, mMtu); - } + return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, + mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, + mPcscfAddresses, mMtu, mMtuV4, mMtuV6); } } } diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp index f90f26f00e6d..75db55122553 100644 --- a/tests/AppLaunch/Android.bp +++ b/tests/AppLaunch/Android.bp @@ -8,6 +8,8 @@ android_test { "android.test.base", "android.test.runner", ], - static_libs: ["androidx.test.rules"], + static_libs: [ + "androidx.test.rules", + "ub-uiautomator"], test_suites: ["device-tests"], } diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 2d2f4dbdf907..7d750b7bf690 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -15,6 +15,8 @@ */ package com.android.tests.applaunch; +import static org.junit.Assert.assertNotNull; + import android.accounts.Account; import android.accounts.AccountManager; import android.app.ActivityManager; @@ -29,7 +31,9 @@ import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; +import android.support.test.uiautomator.UiDevice; import android.test.InstrumentationTestCase; import android.test.InstrumentationTestRunner; import android.util.Log; @@ -46,6 +50,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.nio.file.Paths; import java.time.format.DateTimeFormatter; import java.time.ZonedDateTime; import java.time.ZoneOffset; @@ -67,6 +72,7 @@ import java.util.Set; * in the following format: * -e apps <app name>^<result key>|<app name>^<result key> */ +@Deprecated public class AppLaunch extends InstrumentationTestCase { private static final int JOIN_TIMEOUT = 10000; @@ -94,6 +100,9 @@ public class AppLaunch extends InstrumentationTestCase { private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval"; private static final String KEY_COMPILER_FILTERS = "compiler_filters"; private static final String KEY_FORCE_STOP_APP = "force_stop_app"; + private static final String ENABLE_SCREEN_RECORDING = "enable_screen_recording"; + private static final int MAX_RECORDING_PARTS = 5; + private static final long VIDEO_TAIL_BUFFER = 500; private static final String SIMPLEPERF_APP_CMD = "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s"; @@ -144,14 +153,17 @@ public class AppLaunch extends InstrumentationTestCase { private Map<String, Intent> mNameToIntent; private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>(); + private RecordingThread mCurrentThread; private Map<String, String> mNameToResultKey; private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime; private IActivityManager mAm; + private File launchSubDir = null; private String mSimplePerfCmd = null; private String mLaunchOrder = null; private boolean mDropCache = false; private int mLaunchIterations = 10; private boolean mForceStopApp = true; + private boolean mEnableRecording = false; private int mTraceLaunchCount = 0; private String mTraceDirectoryStr = null; private Bundle mResult = new Bundle(); @@ -166,6 +178,7 @@ public class AppLaunch extends InstrumentationTestCase { private boolean mCycleCleanUp = false; private boolean mTraceAll = false; private boolean mIterationCycle = false; + private UiDevice mDevice; enum IorapStatus { UNDEFINED, @@ -222,7 +235,7 @@ public class AppLaunch extends InstrumentationTestCase { } try { - File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY); + launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY); if (!launchSubDir.exists() && !launchSubDir.mkdirs()) { throw new IOException("Unable to create the lauch file sub directory " @@ -923,9 +936,16 @@ public class AppLaunch extends InstrumentationTestCase { mLaunchIterations = Integer.parseInt(launchIterations); } String forceStopApp = args.getString(KEY_FORCE_STOP_APP); + if (forceStopApp != null) { mForceStopApp = Boolean.parseBoolean(forceStopApp); } + + String enableRecording = args.getString(ENABLE_SCREEN_RECORDING); + + if (enableRecording != null) { + mEnableRecording = Boolean.parseBoolean(enableRecording); + } String appList = args.getString(KEY_APPS); if (appList == null) return; @@ -1038,6 +1058,9 @@ public class AppLaunch extends InstrumentationTestCase { private AppLaunchResult startApp(String appName, String launchReason) throws NameNotFoundException, RemoteException { Log.i(TAG, "Starting " + appName); + if(mEnableRecording) { + startRecording(appName, launchReason); + } Intent startIntent = mNameToIntent.get(appName); if (startIntent == null) { @@ -1053,6 +1076,10 @@ public class AppLaunch extends InstrumentationTestCase { } catch (InterruptedException e) { // ignore } + + if(mEnableRecording) { + stopRecording(); + } return runnable.getResult(); } @@ -1360,4 +1387,126 @@ public class AppLaunch extends InstrumentationTestCase { } } + + /** + * Start the screen recording while launching the app. + * + * @param appName + * @param launchReason + */ + private void startRecording(String appName, String launchReason) { + Log.v(TAG, "Started Recording"); + mCurrentThread = new RecordingThread("test-screen-record", + String.format("%s_%s", appName, launchReason)); + mCurrentThread.start(); + } + + /** + * Stop already started screen recording. + */ + private void stopRecording() { + // Skip if not directory. + if (launchSubDir == null) { + return; + } + + // Add some extra time to the video end. + SystemClock.sleep(VIDEO_TAIL_BUFFER); + // Ctrl + C all screen record processes. + mCurrentThread.cancel(); + // Wait for the thread to completely die. + try { + mCurrentThread.join(); + } catch (InterruptedException ex) { + Log.e(TAG, "Interrupted when joining the recording thread.", ex); + } + Log.v(TAG, "Stopped Recording"); + } + + /** Returns the recording's name for part {@code part} of launch description. */ + private File getOutputFile(String description, int part) { + // Omit the iteration number for the first iteration. + final String fileName = + String.format( + "%s-video%s.mp4", description, part == 1 ? "" : part); + return Paths.get(launchSubDir.getAbsolutePath(), description).toFile(); + } + + + /** + * Encapsulates the start and stop screen recording logic. + * Copied from ScreenRecordCollector. + */ + private class RecordingThread extends Thread { + private final String mDescription; + private final List<File> mRecordings; + + private boolean mContinue; + + public RecordingThread(String name, String description) { + super(name); + + mContinue = true; + mRecordings = new ArrayList<>(); + + assertNotNull("No test description provided for recording.", description); + mDescription = description; + } + + @Override + public void run() { + try { + // Start at i = 1 to encode parts as X.mp4, X2.mp4, X3.mp4, etc. + for (int i = 1; i <= MAX_RECORDING_PARTS && mContinue; i++) { + File output = getOutputFile(mDescription, i); + Log.d( + TAG, + String.format("Recording screen to %s", output.getAbsolutePath())); + mRecordings.add(output); + // Make sure not to block on this background command in the main thread so + // that the test continues to run, but block in this thread so it does not + // trigger a new screen recording session before the prior one completes. + getDevice().executeShellCommand( + String.format("screenrecord %s", output.getAbsolutePath())); + } + } catch (IOException e) { + throw new RuntimeException("Caught exception while screen recording."); + } + } + + public void cancel() { + mContinue = false; + + // Identify the screenrecord PIDs and send SIGINT 2 (Ctrl + C) to each. + try { + String[] pids = getDevice().executeShellCommand( + "pidof screenrecord").split(" "); + for (String pid : pids) { + // Avoid empty process ids, because of weird splitting behavior. + if (pid.isEmpty()) { + continue; + } + + getDevice().executeShellCommand( + String.format("kill -2 %s", pid)); + Log.d( + TAG, + String.format("Sent SIGINT 2 to screenrecord process (%s)", pid)); + } + } catch (IOException e) { + throw new RuntimeException("Failed to kill screen recording process."); + } + } + + public List<File> getRecordings() { + return mRecordings; + } + } + + public UiDevice getDevice() { + if (mDevice == null) { + mDevice = UiDevice.getInstance(getInstrumentation()); + } + return mDevice; + } } diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java index f4f610b1b280..fa292bd0d57a 100644 --- a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java +++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java @@ -100,7 +100,6 @@ public class DozeTestDream extends DreamService { public void onAttachedToWindow() { super.onAttachedToWindow(); setInteractive(false); - setLowProfile(true); setFullscreen(true); setContentView(R.layout.dream); setScreenBright(false); |