diff options
92 files changed, 1258 insertions, 4798 deletions
diff --git a/Android.bp b/Android.bp index 057b1d62ea5a..59e903ef37d3 100644 --- a/Android.bp +++ b/Android.bp @@ -389,7 +389,6 @@ java_defaults { // TODO(b/120066492): remove gps_debug and protolog.conf.json when the build // system propagates "required" properly. "gps_debug.conf", - "protolog.conf.json.gz", "core.protolog.pb", "framework-res", // any install dependencies should go into framework-minus-apex-install-dependencies diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 5b32f33777fa..c00e6101b363 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -1757,7 +1757,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mCallbacks, result.getSequenceId()); } if ((!mSingleCapture) && (mPreviewProcessorType == - IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY)) { + IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) + && mInitialized) { CaptureStageImpl captureStage = null; try { captureStage = mPreviewRequestUpdateProcessor.process( @@ -1780,8 +1781,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } else { mRequestUpdatedNeeded = false; } - } else if (mPreviewProcessorType == - IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR) { + } else if ((mPreviewProcessorType == + IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR) && mInitialized) { int idx = mPendingResultMap.indexOfKey(timestamp); if ((idx >= 0) && (mPendingResultMap.get(timestamp).first == null)) { @@ -1828,7 +1829,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } else { // No special handling for PROCESSOR_TYPE_NONE } - if (notifyClient) { + if (notifyClient && mInitialized) { final long ident = Binder.clearCallingIdentity(); try { if (processStatus) { diff --git a/core/java/android/hardware/radio/Announcement.java b/core/java/android/hardware/radio/Announcement.java index 3ba3ebceeb18..faa103cf1da3 100644 --- a/core/java/android/hardware/radio/Announcement.java +++ b/core/java/android/hardware/radio/Announcement.java @@ -71,7 +71,7 @@ public final class Announcement implements Parcelable { /** * An event called whenever a list of active announcements change. * - * The entire list is sent each time a new announcement appears or any ends broadcasting. + * <p>The entire list is sent each time a new announcement appears or any ends broadcasting. * * @param activeAnnouncements a full list of active announcements */ diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java index c5167dbc7d4c..6146df8b4b1b 100644 --- a/core/java/android/hardware/radio/ProgramList.java +++ b/core/java/android/hardware/radio/ProgramList.java @@ -357,7 +357,7 @@ public final class ProgramList implements AutoCloseable { /** * Constructor of program list filter. * - * Arrays passed to this constructor become owned by this object, do not modify them later. + * <p>Arrays passed to this constructor will be owned by this object, do not modify them. * * @param identifierTypes see getIdentifierTypes() * @param identifiers see getIdentifiers() @@ -438,12 +438,11 @@ public final class ProgramList implements AutoCloseable { /** * Returns the list of identifier types that satisfy the filter. * - * If the program list entry contains at least one identifier of the type - * listed, it satisfies this condition. + * <p>If the program list entry contains at least one identifier of the type + * listed, it satisfies this condition. Empty list means no filtering on + * identifier type. * - * Empty list means no filtering on identifier type. - * - * @return the list of accepted identifier types, must not be modified + * @return the set of accepted identifier types, must not be modified */ public @NonNull Set<Integer> getIdentifierTypes() { return mIdentifierTypes; @@ -452,12 +451,10 @@ public final class ProgramList implements AutoCloseable { /** * Returns the list of identifiers that satisfy the filter. * - * If the program list entry contains at least one listed identifier, - * it satisfies this condition. - * - * Empty list means no filtering on identifier. + * <p>If the program list entry contains at least one listed identifier, + * it satisfies this condition. Empty list means no filtering on identifier. * - * @return the list of accepted identifiers, must not be modified + * @return the set of accepted identifiers, must not be modified */ public @NonNull Set<ProgramSelector.Identifier> getIdentifiers() { return mIdentifiers; @@ -476,7 +473,7 @@ public final class ProgramList implements AutoCloseable { /** * Checks, if updates on entry modifications should be disabled. * - * If true, 'modified' vector of ProgramListChunk must contain list + * <p>If true, 'modified' vector of ProgramListChunk must contain list * additions only. Once the program is added to the list, it's not * updated anymore. */ diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index 0740374ad8e2..42028f67f400 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -36,27 +36,31 @@ import java.util.stream.Stream; /** * A set of identifiers necessary to tune to a given station. * - * This can hold various identifiers, like - * - AM/FM frequency - * - HD Radio subchannel - * - DAB channel info + * <p>This can hold various identifiers, like + * <ui> + * <li>AM/FM frequency</li> + * <li>HD Radio subchannel</li> + * <li>DAB channel info</li> + * </ui> * - * The primary ID uniquely identifies a station and can be used for equality + * <p>The primary ID uniquely identifies a station and can be used for equality * check. The secondary IDs are supplementary and can speed up tuning process, * but the primary ID is sufficient (ie. after a full band scan). * - * Two selectors with different secondary IDs, but the same primary ID are + * <p>Two selectors with different secondary IDs, but the same primary ID are * considered equal. In particular, secondary IDs vector may get updated for * an entry on the program list (ie. when a better frequency for a given * station is found). * - * The primaryId of a given programType MUST be of a specific type: - * - AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise; - * - AM_HD, FM_HD: HD_STATION_ID_EXT; - * - DAB: DAB_SIDECC; - * - DRMO: DRMO_SERVICE_ID; - * - SXM: SXM_SERVICE_ID; - * - VENDOR: VENDOR_PRIMARY. + * <p>The primaryId of a given programType MUST be of a specific type: + * <ui> + * <li>AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise;</li> + * <li>AM_HD, FM_HD: HD_STATION_ID_EXT;</li> + * <li>DAB: DAB_SIDECC;</li> + * <li>DRMO: DRMO_SERVICE_ID;</li> + * <li>SXM: SXM_SERVICE_ID;</li> + * <li>VENDOR: VENDOR_PRIMARY.</li> + * </ui> * @hide */ @SystemApi @@ -258,10 +262,10 @@ public final class ProgramSelector implements Parcelable { /** * 64bit additional identifier for HD Radio. * - * <p>Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not - * globally unique. To provide a best-effort solution, a short version of - * station name may be carried as additional identifier and may be used - * by the tuner hardware to double-check tuning. + * <p>Due to Station ID abuse, some {@link #IDENTIFIER_TYPE_HD_STATION_ID_EXT} + * identifiers may be not globally unique. To provide a best-effort solution, a + * short version of station name may be carried as additional identifier and + * may be used by the tuner hardware to double-check tuning. * * <p>The name is limited to the first 8 A-Z0-9 characters (lowercase * letters must be converted to uppercase). Encoded in little-endian @@ -384,7 +388,7 @@ public final class ProgramSelector implements Parcelable { * The value format is determined by a vendor. * * <p>It must not be used in any other programType than corresponding VENDOR - * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must + * type between VENDOR_START and VENDOR_END (e.g. identifier type 1015 must * not be used in any program type other than 1015). */ public static final int IDENTIFIER_TYPE_VENDOR_START = PROGRAM_TYPE_VENDOR_START; @@ -435,9 +439,10 @@ public final class ProgramSelector implements Parcelable { /** * Constructor for ProgramSelector. * - * It's not desired to modify selector objects, so all its fields are initialized at creation. + * <p>It's not desired to modify selector objects, so all its fields are initialized at + * creation. * - * Identifier lists must not contain any nulls, but can itself be null to be interpreted + * <p>Identifier lists must not contain any nulls, but can itself be null to be interpreted * as empty list at object creation. * * @param programType type of a radio technology. @@ -492,8 +497,8 @@ public final class ProgramSelector implements Parcelable { /** * Looks up an identifier of a given type (either primary or secondary). * - * If there are multiple identifiers if a given type, then first in order (where primary id is - * before any secondary) is selected. + * <p>If there are multiple identifiers if a given type, then first in order (where primary id + * is before any secondary) is selected. * * @param type type of identifier. * @return identifier value, if found. @@ -510,11 +515,11 @@ public final class ProgramSelector implements Parcelable { /** * Looks up all identifier of a given type (either primary or secondary). * - * Some identifiers may be provided multiple times, for example - * IDENTIFIER_TYPE_AMFM_FREQUENCY for FM Alternate Frequencies. + * <p>Some identifiers may be provided multiple times, for example + * {@link #IDENTIFIER_TYPE_AMFM_FREQUENCY} for FM Alternate Frequencies. * * @param type type of identifier. - * @return a list of identifiers, generated on each call. May be modified. + * @return an array of identifiers, generated on each call. May be modified. */ public @NonNull Identifier[] getAllIds(@IdentifierType int type) { List<Identifier> out = new ArrayList<>(); @@ -543,14 +548,14 @@ public final class ProgramSelector implements Parcelable { /** * Creates an equivalent ProgramSelector with a given secondary identifier preferred. * - * Used to point to a specific physical identifier for technologies that may broadcast the same - * program on different channels. For example, with a DAB program broadcasted over multiple + * <p>Used to point to a specific physical identifier for technologies that may broadcast the + * same program on different channels. For example, with a DAB program broadcasted over multiple * ensembles, the radio hardware may select the one with the strongest signal. The UI may select * preferred ensemble though, so the radio hardware may try to use it in the first place. * - * This is a best-effort hint for the tuner, not a guaranteed behavior. + * <p>This is a best-effort hint for the tuner, not a guaranteed behavior. * - * Setting the given secondary identifier as preferred means filtering out other secondary + * <p>Setting the given secondary identifier as preferred means filtering out other secondary * identifiers of its type and adding it to the list. * * @param preferred preferred secondary identifier @@ -577,7 +582,7 @@ public final class ProgramSelector implements Parcelable { * * @param band the band. * @param frequencyKhz the frequency in kHz. - * @return new ProgramSelector object representing given frequency. + * @return new {@link ProgramSelector} object representing given frequency. * @throws IllegalArgumentException if provided frequency is out of bounds. */ public static @NonNull ProgramSelector createAmFmSelector( @@ -588,13 +593,13 @@ public final class ProgramSelector implements Parcelable { /** * Checks, if a given AM/FM frequency is roughly valid and in correct unit. * - * It does not check the range precisely: it may provide false positives, but not false + * <p>It does not check the range precisely: it may provide false positives, but not false * negatives. In particular, it may be way off for certain regions. - * The main purpose is to avoid passing inproper units, ie. MHz instead of kHz. + * The main purpose is to avoid passing improper units, ie. MHz instead of kHz. * * @param isAm true, if AM, false if FM. * @param frequencyKhz the frequency in kHz. - * @return true, if the frequency is rougly valid. + * @return true, if the frequency is roughly valid. */ private static boolean isValidAmFmFrequency(boolean isAm, int frequencyKhz) { if (isAm) { @@ -607,7 +612,7 @@ public final class ProgramSelector implements Parcelable { /** * Builds new ProgramSelector for AM/FM frequency. * - * This method variant supports HD Radio subchannels, but it's undesirable to + * <p>This method variant supports HD Radio subchannels, but it's undesirable to * select them manually. Instead, the value should be retrieved from program list. * * @param band the band. @@ -741,9 +746,9 @@ public final class ProgramSelector implements Parcelable { }; /** - * A single program identifier component, eg. frequency or channel ID. + * A single program identifier component, e.g. frequency or channel ID. * - * The long value field holds the value in format described in comments for + * <p>The long value field holds the value in format described in comments for * IdentifierType constants. */ public static final class Identifier implements Parcelable { @@ -776,11 +781,11 @@ public final class ProgramSelector implements Parcelable { } /** - * Returns whether this Identifier's type is considered a category when filtering + * Returns whether this identifier's type is considered a category when filtering * ProgramLists for category entries. * * @see ProgramList.Filter#areCategoriesIncluded - * @return False if this identifier's type is not tuneable (e.g. DAB ensemble or + * @return False if this identifier's type is not tunable (e.g. DAB ensemble or * vendor-specified type). True otherwise. */ public boolean isCategoryType() { @@ -791,14 +796,14 @@ public final class ProgramSelector implements Parcelable { /** * Value of an identifier. * - * Its meaning depends on identifier type, ie. for IDENTIFIER_TYPE_AMFM_FREQUENCY type, - * the value is a frequency in kHz. + * <p>Its meaning depends on identifier type, ie. for + * {@link #IDENTIFIER_TYPE_AMFM_FREQUENCY} type, the value is a frequency in kHz. * - * The range of a value depends on its type; it does not always require the whole long + * <p>The range of a value depends on its type; it does not always require the whole long * range. Casting to necessary type (ie. int) without range checking is correct in front-end * code - any range violations are either errors in the framework or in the - * HAL implementation. For example, IDENTIFIER_TYPE_AMFM_FREQUENCY always fits in int, - * as Integer.MAX_VALUE would mean 2.1THz. + * HAL implementation. For example, {@link #IDENTIFIER_TYPE_AMFM_FREQUENCY} always fits in + * int, as {@link Integer#MAX_VALUE} would mean 2.1THz. * * @return value of an identifier. */ diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index da6c68646820..61854e44287b 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -102,7 +102,7 @@ public class RadioManager { public @interface RadioStatusType{} - // keep in sync with radio_class_t in /system/core/incluse/system/radio.h + // keep in sync with radio_class_t in /system/core/include/system/radio.h /** Radio module class supporting FM (including HD radio) and AM */ public static final int CLASS_AM_FM = 0; /** Radio module class supporting satellite radio */ @@ -154,7 +154,7 @@ public class RadioManager { /** * Forces mono audio stream reception. * - * Analog broadcasts can recover poor reception conditions by jointing + * <p>Analog broadcasts can recover poor reception conditions by jointing * stereo channels into one. Mainly for, but not limited to AM/FM. */ public static final int CONFIG_FORCE_MONO = 1; @@ -176,7 +176,7 @@ public class RadioManager { /** * Forces the digital playback for the supporting radio technology. * - * User may disable digital-analog handover that happens with poor + * <p>User may disable digital-analog handover that happens with poor * reception conditions. With digital forced, the radio will remain silent * instead of switching to analog channel if it's available. This is purely * user choice, it does not reflect the actual state of handover. @@ -185,7 +185,7 @@ public class RadioManager { /** * RDS Alternative Frequencies. * - * If set and the currently tuned RDS station broadcasts on multiple + * <p>If set and the currently tuned RDS station broadcasts on multiple * channels, radio tuner automatically switches to the best available * alternative. */ @@ -193,7 +193,7 @@ public class RadioManager { /** * RDS region-specific program lock-down. * - * Allows user to lock to the current region as they move into the + * <p>Allows user to lock to the current region as they move into the * other region. */ public static final int CONFIG_RDS_REG = 5; @@ -247,11 +247,12 @@ public class RadioManager { @Retention(RetentionPolicy.SOURCE) public @interface ConfigFlag {} - /***************************************************************************** + /** * Lists properties, options and radio bands supported by a given broadcast radio module. - * Each module has a unique ID used to address it when calling RadioManager APIs. - * Module properties are returned by {@link #listModules(List <ModuleProperties>)} method. - ****************************************************************************/ + * + * <p>Each module has a unique ID used to address it when calling RadioManager APIs. + * Module properties are returned by {@link #listModules(List)} method. + */ public static class ModuleProperties implements Parcelable { private final int mId; @@ -315,8 +316,11 @@ public class RadioManager { return set.stream().mapToInt(Integer::intValue).toArray(); } - /** Unique module identifier provided by the native service. - * For use with {@link #openTuner(int, BandConfig, boolean, RadioTuner.Callback, Handler)}. + /** + * Unique module identifier provided by the native service. + * + * <p>or use with + * {@link #openTuner(int, BandConfig, boolean, RadioTuner.Callback, Handler)}. * @return the radio module unique identifier. */ public int getId() { @@ -324,22 +328,24 @@ public class RadioManager { } /** - * Module service (driver) name as registered with HIDL. + * Module service (driver) name as registered with HIDL or AIDL HAL. * @return the module service name. */ public @NonNull String getServiceName() { return mServiceName; } - /** Module class identifier: {@link #CLASS_AM_FM}, {@link #CLASS_SAT}, {@link #CLASS_DT} + /** + * Module class identifier: {@link #CLASS_AM_FM}, {@link #CLASS_SAT}, {@link #CLASS_DT} * @return the radio module class identifier. */ public int getClassId() { return mClassId; } - /** Human readable broadcast radio module implementor - * @return the name of the radio module implementator. + /** + * Human readable broadcast radio module implementor + * @return the name of the radio module implementer. */ public String getImplementor() { return mImplementor; @@ -352,31 +358,38 @@ public class RadioManager { return mProduct; } - /** Human readable broadcast radio module version number + /** + * Human readable broadcast radio module version number * @return the radio module version. */ public String getVersion() { return mVersion; } - /** Radio module serial number. - * Can be used for subscription services. + /** + * Radio module serial number. + * + * <p>This can be used for subscription services. * @return the radio module serial number. */ public String getSerial() { return mSerial; } - /** Number of tuners available. - * This is the number of tuners that can be open simultaneously. + /** + * Number of tuners available. + * + * <p>This is the number of tuners that can be open simultaneously. * @return the number of tuners supported. */ public int getNumTuners() { return mNumTuners; } - /** Number tuner audio sources available. Must be less or equal to getNumTuners(). - * When more than one tuner is supported, one is usually for playback and has one + /** + * Number tuner audio sources available. Must be less or equal to {@link #getNumTuners}. + * + * <p>When more than one tuner is supported, one is usually for playback and has one * associated audio source and the other is for pre scanning and building a * program list. * @return the number of audio sources available. @@ -387,20 +400,24 @@ public class RadioManager { } /** - * Checks, if BandConfig initialization (after {@link RadioManager#openTuner}) + * Checks, if {@link BandConfig} initialization (after {@link RadioManager#openTuner}) * is required to be done before other operations or not. * - * If it is, the client has to wait for {@link RadioTuner.Callback#onConfigurationChanged} - * callback before executing any other operations. Otherwise, such operation will fail - * returning {@link RadioManager#STATUS_INVALID_OPERATION} error code. + * <p>If it is, the client has to wait for + * {@link RadioTuner.Callback#onConfigurationChanged} callback before executing any other + * operations. Otherwise, such operation will fail returning + * {@link RadioManager#STATUS_INVALID_OPERATION} error code. */ public boolean isInitializationRequired() { return mIsInitializationRequired; } - /** {@code true} if audio capture is possible from radio tuner output. - * This indicates if routing to audio devices not connected to the same HAL as the FM radio - * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented. + /** + * {@code true} if audio capture is possible from radio tuner output. + * + * <p>This indicates if routing to audio devices not connected to the same HAL as the FM + * radio is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be + * implemented. * @return {@code true} if audio capture is possible, {@code false} otherwise. */ public boolean isCaptureSupported() { @@ -421,8 +438,8 @@ public class RadioManager { /** * Checks, if a given program type is supported by this tuner. * - * If a program type is supported by radio module, it means it can tune - * to ProgramSelector of a given type. + * <p>If a program type is supported by radio module, it means it can tune + * to {@link ProgramSelector} of a given type. * * @return {@code true} if a given program type is supported. */ @@ -433,8 +450,8 @@ public class RadioManager { /** * Checks, if a given program identifier is supported by this tuner. * - * If an identifier is supported by radio module, it means it can use it for - * tuning to ProgramSelector with either primary or secondary Identifier of + * <p>If an identifier is supported by radio module, it means it can use it for + * tuning to {@link ProgramSelector} with either primary or secondary Identifier of * a given type. * * @return {@code true} if a given program type is supported. @@ -446,9 +463,9 @@ public class RadioManager { /** * A frequency table for Digital Audio Broadcasting (DAB). * - * The key is a channel name, i.e. 5A, 7B. + * <p>The key is a channel name, i.e. 5A, 7B. * - * The value is a frequency, in kHz. + * <p>The value is a frequency, in kHz. * * @return a frequency table, or {@code null} if the module doesn't support DAB */ @@ -460,17 +477,18 @@ public class RadioManager { * A map of vendor-specific opaque strings, passed from HAL without changes. * Format of these strings can vary across vendors. * - * It may be used for extra features, that's not supported by a platform, + * <p>It may be used for extra features, that's not supported by a platform, * for example: preset-slots=6; ultra-hd-capable=false. * - * Keys must be prefixed with unique vendor Java-style namespace, - * eg. 'com.somecompany.parameter1'. + * <p>Keys must be prefixed with unique vendor Java-style namespace, + * e.g. 'com.somecompany.parameter1'. */ public @NonNull Map<String, String> getVendorInfo() { return mVendorInfo; } - /** List of descriptors for all bands supported by this module. + /** + * List of descriptors for all bands supported by this module. * @return an array of {@link BandDescriptor}. */ public BandDescriptor[] getBands() { @@ -590,7 +608,9 @@ public class RadioManager { } /** Radio band descriptor: an element in ModuleProperties bands array. - * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */ + * + * <p>It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} + */ public static class BandDescriptor implements Parcelable { private final int mRegion; @@ -610,16 +630,18 @@ public class RadioManager { mSpacing = spacing; } - /** Region this band applies to. E.g. {@link #REGION_ITU_1} + /** + * Region this band applies to. E.g. {@link #REGION_ITU_1} * @return the region this band is associated to. */ public int getRegion() { return mRegion; } - /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: + /** + * Band type, e.g. {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: * <ul> - * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> - * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> + * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}</li> + * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}</li> * </ul> * @return the band type. */ @@ -645,23 +667,29 @@ public class RadioManager { return mType == BAND_FM || mType == BAND_FM_HD; } - /** Lower band limit expressed in units according to band type. - * Currently all defined band types express channels as frequency in kHz + /** + * Lower band limit expressed in units according to band type. + * + * <p>Currently all defined band types express channels as frequency in kHz. * @return the lower band limit. */ public int getLowerLimit() { return mLowerLimit; } - /** Upper band limit expressed in units according to band type. - * Currently all defined band types express channels as frequency in kHz + /** + * Upper band limit expressed in units according to band type. + * + * <p>Currently all defined band types express channels as frequency in kHz. * @return the upper band limit. */ public int getUpperLimit() { return mUpperLimit; } - /** Channel spacing in units according to band type. - * Currently all defined band types express channels as frequency in kHz - * @return the channel spacing. + /** + * Channel spacing in units according to band type. + * + * <p>Currently all defined band types express channels as frequency in kHz + * @return the channel spacing.</p> */ public int getSpacing() { return mSpacing; @@ -758,9 +786,11 @@ public class RadioManager { } } - /** FM band descriptor + /** + * FM band descriptor * @see #BAND_FM - * @see #BAND_FM_HD */ + * @see #BAND_FM_HD + */ public static class FmBandDescriptor extends BandDescriptor { private final boolean mStereo; private final boolean mRds; @@ -779,19 +809,22 @@ public class RadioManager { mEa = ea; } - /** Stereo is supported + /** + * Stereo is supported * @return {@code true} if stereo is supported, {@code false} otherwise. */ public boolean isStereoSupported() { return mStereo; } - /** RDS or RBDS(if region is ITU2) is supported + /** + * RDS or RBDS(if region is ITU2) is supported * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise. */ public boolean isRdsSupported() { return mRds; } - /** Traffic announcement is supported + /** + * Traffic announcement is supported * @return {@code true} if TA is supported, {@code false} otherwise. */ public boolean isTaSupported() { @@ -804,8 +837,9 @@ public class RadioManager { return mAf; } - /** Emergency Announcement is supported - * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise. + /** + * Emergency Announcement is supported + * @return {@code true} if Emergency announcement is supported, {@code false} otherwise. */ public boolean isEaSupported() { return mEa; @@ -890,8 +924,10 @@ public class RadioManager { } } - /** AM band descriptor. - * @see #BAND_AM */ + /** + * AM band descriptor. + * @see #BAND_AM + */ public static class AmBandDescriptor extends BandDescriptor { private final boolean mStereo; @@ -903,8 +939,9 @@ public class RadioManager { mStereo = stereo; } - /** Stereo is supported - * @return {@code true} if stereo is supported, {@code false} otherwise. + /** + * Stereo is supported + * @return {@code true} if stereo is supported, {@code false} otherwise. */ public boolean isStereoSupported() { return mStereo; @@ -991,39 +1028,47 @@ public class RadioManager { return mDescriptor; } - /** Region this band applies to. E.g. {@link #REGION_ITU_1} - * @return the region associated with this band. + /** + * Region this band applies to. E.g. {@link #REGION_ITU_1} + * @return the region associated with this band. */ public int getRegion() { return mDescriptor.getRegion(); } - /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: + /** + * Band type, e.g. {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: * <ul> - * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> - * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> + * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}</li> + * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}</li> * </ul> * @return the band type. */ public int getType() { return mDescriptor.getType(); } - /** Lower band limit expressed in units according to band type. - * Currently all defined band types express channels as frequency in kHz - * @return the lower band limit. + /** + * Lower band limit expressed in units according to band type. + * + * <p>Currently all defined band types express channels as frequency in kHz. + * @return the lower band limit. */ public int getLowerLimit() { return mDescriptor.getLowerLimit(); } - /** Upper band limit expressed in units according to band type. - * Currently all defined band types express channels as frequency in kHz - * @return the upper band limit. + /** + * Upper band limit expressed in units according to band type. + * + * <p>Currently all defined band types express channels as frequency in kHz. + * @return the upper band limit. */ public int getUpperLimit() { return mDescriptor.getUpperLimit(); } - /** Channel spacing in units according to band type. - * Currently all defined band types express channels as frequency in kHz - * @return the channel spacing. + /** + * Channel spacing in units according to band type. + * + * <p>Currently all defined band types express channels as frequency in kHz. + * @return the channel spacing. */ public int getSpacing() { return mDescriptor.getSpacing(); @@ -1089,9 +1134,11 @@ public class RadioManager { } } - /** FM band configuration. + /** + * FM band configuration. * @see #BAND_FM - * @see #BAND_FM_HD */ + * @see #BAND_FM_HD + */ public static class FmBandConfig extends BandConfig { private final boolean mStereo; private final boolean mRds; @@ -1119,28 +1166,32 @@ public class RadioManager { mEa = ea; } - /** Get stereo enable state + /** + * Get stereo enable state * @return the enable state. */ public boolean getStereo() { return mStereo; } - /** Get RDS or RBDS(if region is ITU2) enable state + /** + * Get RDS or RBDS(if region is ITU2) enable state * @return the enable state. */ public boolean getRds() { return mRds; } - /** Get Traffic announcement enable state + /** + * Get Traffic announcement enable state * @return the enable state. */ public boolean getTa() { return mTa; } - /** Get Alternate Frequency Switching enable state + /** + * Get Alternate Frequency Switching enable state * @return the enable state. */ public boolean getAf() { @@ -1285,7 +1336,8 @@ public class RadioManager { return config; } - /** Set stereo enable state + /** + * Set stereo enable state * @param state The new enable state. * @return the same Builder instance. */ @@ -1294,7 +1346,8 @@ public class RadioManager { return this; } - /** Set RDS or RBDS(if region is ITU2) enable state + /** + * Set RDS or RBDS(if region is ITU2) enable state * @param state The new enable state. * @return the same Builder instance. */ @@ -1303,7 +1356,8 @@ public class RadioManager { return this; } - /** Set Traffic announcement enable state + /** + * Set Traffic announcement enable state * @param state The new enable state. * @return the same Builder instance. */ @@ -1312,7 +1366,8 @@ public class RadioManager { return this; } - /** Set Alternate Frequency Switching enable state + /** + * Set Alternate Frequency Switching enable state * @param state The new enable state. * @return the same Builder instance. */ @@ -1321,7 +1376,8 @@ public class RadioManager { return this; } - /** Set Emergency Announcement enable state + /** + * Set Emergency Announcement enable state * @param state The new enable state. * @return the same Builder instance. */ @@ -1332,8 +1388,10 @@ public class RadioManager { }; } - /** AM band configuration. - * @see #BAND_AM */ + /** + * AM band configuration. + * @see #BAND_AM + */ public static class AmBandConfig extends BandConfig { private final boolean mStereo; @@ -1349,7 +1407,8 @@ public class RadioManager { mStereo = stereo; } - /** Get stereo enable state + /** + * Get stereo enable state * @return the enable state. */ public boolean getStereo() { @@ -1453,7 +1512,8 @@ public class RadioManager { return config; } - /** Set stereo enable state + /** + * Set stereo enable state * @param state The new enable state. * @return the same Builder instance. */ @@ -1467,7 +1527,8 @@ public class RadioManager { /** Radio program information. */ public static class ProgramInfo implements Parcelable { - // sourced from hardware/interfaces/broadcastradio/2.0/types.hal + // sourced from + // hardware/interfaces/broadcastradio/aidl/android/hardware/broadcastradio/ProgramInfo.aidl private static final int FLAG_LIVE = 1 << 0; private static final int FLAG_MUTED = 1 << 1; private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2; @@ -1521,10 +1582,10 @@ public class RadioManager { /** * Identifier currently used for program selection. * - * This identifier can be used to determine which technology is + * <p>This identifier can be used to determine which technology is * currently being used for reception. * - * Some program selectors contain tuning information for different radio + * <p>Some program selectors contain tuning information for different radio * technologies (i.e. FM RDS and DAB). For example, user may tune using * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware * may choose to use DAB technology to make actual tuning. This identifier @@ -1537,7 +1598,7 @@ public class RadioManager { /** * Identifier currently used by hardware to physically tune to a channel. * - * Some radio technologies broadcast the same program on multiple channels, + * <p>Some radio technologies broadcast the same program on multiple channels, * i.e. with RDS AF the same program may be broadcasted on multiple * alternative frequencies; the same DAB program may be broadcast on * multiple ensembles. This identifier points to the channel to which the @@ -1550,11 +1611,11 @@ public class RadioManager { /** * Primary identifiers of related contents. * - * Some radio technologies provide pointers to other programs that carry + * <p>Some radio technologies provide pointers to other programs that carry * related content (i.e. DAB soft-links). This field is a list of pointers * to other programs on the program list. * - * Please note, that these identifiers does not have to exist on the program + * <p>Please note, that these identifiers does not have to exist on the program * list - i.e. DAB tuner may provide information on FM RDS alternatives * despite not supporting FM RDS. If the system has multiple tuners, another * one may have it on its list. @@ -1563,7 +1624,8 @@ public class RadioManager { return mRelatedContent; } - /** Main channel expressed in units according to band type. + /** + * Main channel expressed in units according to band type. * Currently all defined band types express channels as frequency in kHz * @return the program channel * @deprecated Use {@link ProgramInfo#getSelector} instead. @@ -1578,7 +1640,8 @@ public class RadioManager { } } - /** Sub channel ID. E.g 1 for HD radio HD1 + /** + * Sub channel ID. E.g. 1 for HD radio HD1 * @return the program sub channel * @deprecated Use {@link ProgramInfo#getSelector} instead. */ @@ -1600,14 +1663,16 @@ public class RadioManager { return (mInfoFlags & FLAG_TUNED) != 0; } - /** {@code true} if the received program is stereo + /** + * {@code true} if the received program is stereo * @return {@code true} if stereo, {@code false} otherwise. */ public boolean isStereo() { return (mInfoFlags & FLAG_STEREO) != 0; } - /** {@code true} if the received program is digital (e.g HD radio) + /** + * {@code true} if the received program is digital (e.g. HD radio) * @return {@code true} if digital, {@code false} otherwise. * @deprecated Use {@link ProgramInfo#getLogicallyTunedTo()} instead. */ @@ -1623,8 +1688,9 @@ public class RadioManager { /** * {@code true} if the program is currently playing live stream. - * This may result in a slightly altered reception parameters, - * usually targetted at reduced latency. + * + * <p>This may result in a slightly altered reception parameters, + * usually targeted at reduced latency. */ public boolean isLive() { return (mInfoFlags & FLAG_LIVE) != 0; @@ -1634,7 +1700,8 @@ public class RadioManager { * {@code true} if radio stream is not playing, i.e. due to bad reception * conditions or buffering. In this state volume knob MAY be disabled to * prevent user increasing volume too much. - * It does NOT mean the user has muted audio. + * + * <p>It does NOT mean the user has muted audio. */ public boolean isMuted() { return (mInfoFlags & FLAG_MUTED) != 0; @@ -1688,8 +1755,9 @@ public class RadioManager { } /** Metadata currently received from this station. - * null if no metadata have been received - * @return current meta data received from this program. + * + * @return current meta data received from this program, {@code null} if no metadata have + * been received */ public RadioMetadata getMetadata() { return mMetadata; @@ -1699,11 +1767,11 @@ public class RadioManager { * A map of vendor-specific opaque strings, passed from HAL without changes. * Format of these strings can vary across vendors. * - * It may be used for extra features, that's not supported by a platform, + * <p>It may be used for extra features, that's not supported by a platform, * for example: paid-service=true; bitrate=320kbps. * - * Keys must be prefixed with unique vendor Java-style namespace, - * eg. 'com.somecompany.parameter1'. + * <p>Keys must be prefixed with unique vendor Java-style namespace, + * e.g. 'com.somecompany.parameter1'. */ public @NonNull Map<String, String> getVendorInfo() { return mVendorInfo; @@ -1830,13 +1898,14 @@ public class RadioManager { /** * Open an interface to control a tuner on a given broadcast radio module. - * Optionally selects and applies the configuration passed as "config" argument. + * + * <p>Optionally selects and applies the configuration passed as "config" argument. * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory. * @param config desired band and configuration to apply when enabling the hardware module. * optional, can be null. * @param withAudio {@code true} to request a tuner with an audio source. * This tuner is intended for live listening or recording or a radio program. - * If {@code false}, the tuner can only be used to retrieve program informations. + * If {@code false}, the tuner can only be used to retrieve program information. * @param callback {@link RadioTuner.Callback} interface. Mandatory. * @param handler the Handler on which the callbacks will be received. * Can be null if default handler is OK. diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java index 67381ec8e829..31880fd405a8 100644 --- a/core/java/android/hardware/radio/RadioMetadata.java +++ b/core/java/android/hardware/radio/RadioMetadata.java @@ -291,7 +291,7 @@ public final class RadioMetadata implements Parcelable { /** * Provides a Clock that can be used to describe time as provided by the Radio. * - * The clock is defined by the seconds since epoch at the UTC + 0 timezone + * <p>The clock time is defined by the seconds since epoch at the UTC + 0 timezone * and timezone offset from UTC + 0 represented in number of minutes. * * @hide @@ -493,16 +493,16 @@ public final class RadioMetadata implements Parcelable { /** * Retrieves an identifier for a bitmap. * - * The format of an identifier is opaque to the application, + * <p>The format of an identifier is opaque to the application, * with a special case of value 0 being invalid. * An identifier for a given image-tuner pair is unique, so an application * may cache images and determine if there is a necessity to fetch them * again - if identifier changes, it means the image has changed. * - * Only bitmap keys may be used with this method: + * <p>Only bitmap keys may be used with this method: * <ul> - * <li>{@link #METADATA_KEY_ICON}</li> - * <li>{@link #METADATA_KEY_ART}</li> + * <li>{@link #METADATA_KEY_ICON}</li> + * <li>{@link #METADATA_KEY_ART}</li> * </ul> * * @param key The key the value is stored under. @@ -537,7 +537,7 @@ public final class RadioMetadata implements Parcelable { * * <p>Only string array keys may be used with this method: * <ul> - * <li>{@link #METADATA_KEY_UFIDS}</li> + * <li>{@link #METADATA_KEY_UFIDS}</li> * </ul> * * @param key The key the value is stored under @@ -667,17 +667,17 @@ public final class RadioMetadata implements Parcelable { * the METADATA_KEYs defined in this class are used they may only be one * of the following: * <ul> - * <li>{@link #METADATA_KEY_RDS_PS}</li> - * <li>{@link #METADATA_KEY_RDS_RT}</li> - * <li>{@link #METADATA_KEY_TITLE}</li> - * <li>{@link #METADATA_KEY_ARTIST}</li> - * <li>{@link #METADATA_KEY_ALBUM}</li> - * <li>{@link #METADATA_KEY_GENRE}</li> - * <li>{@link #METADATA_KEY_COMMENT_SHORT_DESCRIPTION}</li> - * <li>{@link #METADATA_KEY_COMMENT_ACTUAL_TEXT}</li> - * <li>{@link #METADATA_KEY_COMMERCIAL}</li> - * <li>{@link #METADATA_KEY_HD_STATION_NAME_SHORT}</li> - * <li>{@link #METADATA_KEY_HD_STATION_NAME_LONG}</li> + * <li>{@link #METADATA_KEY_RDS_PS}</li> + * <li>{@link #METADATA_KEY_RDS_RT}</li> + * <li>{@link #METADATA_KEY_TITLE}</li> + * <li>{@link #METADATA_KEY_ARTIST}</li> + * <li>{@link #METADATA_KEY_ALBUM}</li> + * <li>{@link #METADATA_KEY_GENRE}</li> + * <li>{@link #METADATA_KEY_COMMENT_SHORT_DESCRIPTION}</li> + * <li>{@link #METADATA_KEY_COMMENT_ACTUAL_TEXT}</li> + * <li>{@link #METADATA_KEY_COMMERCIAL}</li> + * <li>{@link #METADATA_KEY_HD_STATION_NAME_SHORT}</li> + * <li>{@link #METADATA_KEY_HD_STATION_NAME_LONG}</li> * </ul> * * @param key The key for referencing this value @@ -699,10 +699,10 @@ public final class RadioMetadata implements Parcelable { * the METADATA_KEYs defined in this class are used they may only be one * of the following: * <ul> - * <li>{@link #METADATA_KEY_RDS_PI}</li> - * <li>{@link #METADATA_KEY_RDS_PTY}</li> - * <li>{@link #METADATA_KEY_RBDS_PTY}</li> - * <li>{@link #METADATA_KEY_HD_SUBCHANNELS_AVAILABLE}</li> + * <li>{@link #METADATA_KEY_RDS_PI}</li> + * <li>{@link #METADATA_KEY_RDS_PTY}</li> + * <li>{@link #METADATA_KEY_RBDS_PTY}</li> + * <li>{@link #METADATA_KEY_HD_SUBCHANNELS_AVAILABLE}</li> * </ul> * or any bitmap represented by its identifier. * @@ -720,8 +720,8 @@ public final class RadioMetadata implements Parcelable { * if the METADATA_KEYs defined in this class are used they may only be * one of the following: * <ul> - * <li>{@link #METADATA_KEY_ICON}</li> - * <li>{@link #METADATA_KEY_ART}</li> + * <li>{@link #METADATA_KEY_ICON}</li> + * <li>{@link #METADATA_KEY_ART}</li> * </ul> * <p> * @@ -765,7 +765,7 @@ public final class RadioMetadata implements Parcelable { * the METADATA_KEYs defined in this class are used they may only be one * of the following: * <ul> - * <li>{@link #METADATA_KEY_UFIDS}</li> + * <li>{@link #METADATA_KEY_UFIDS}</li> * </ul> * * @param key The key for referencing this value diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index edb3a641f107..bebb912bd069 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -125,15 +125,15 @@ public final class Trace { @UnsupportedAppUsage @CriticalNative @android.ravenwood.annotation.RavenwoodReplace - private static native boolean nativeIsTagEnabled(long tag); + private static native long nativeGetEnabledTags(); @android.ravenwood.annotation.RavenwoodReplace private static native void nativeSetAppTracingAllowed(boolean allowed); @android.ravenwood.annotation.RavenwoodReplace private static native void nativeSetTracingEnabled(boolean allowed); - private static boolean nativeIsTagEnabled$ravenwood(long traceTag) { + private static long nativeGetEnabledTags$ravenwood() { // Tracing currently completely disabled under Ravenwood - return false; + return 0; } private static void nativeSetAppTracingAllowed$ravenwood(boolean allowed) { @@ -181,7 +181,8 @@ public final class Trace { @UnsupportedAppUsage @SystemApi(client = MODULE_LIBRARIES) public static boolean isTagEnabled(long traceTag) { - return nativeIsTagEnabled(traceTag); + long tags = nativeGetEnabledTags(); + return (tags & traceTag) != 0; } /** diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java index 3e065bf9f450..01b45697f5d4 100644 --- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java +++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java @@ -171,7 +171,9 @@ public class EmphasizedNotificationButton extends Button { return; } - prepareIcon(icon); + if (icon != null) { + prepareIcon(icon); + } mIconToGlue = icon; mGluePending = true; diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp index 422bc1e8b59f..b579daf505e7 100644 --- a/core/jni/android_os_Trace.cpp +++ b/core/jni/android_os_Trace.cpp @@ -124,8 +124,8 @@ static void android_os_Trace_nativeInstantForTrack(JNIEnv* env, jclass, }); } -static jboolean android_os_Trace_nativeIsTagEnabled(JNIEnv* env, jlong tag) { - return tracing_perfetto::isTagEnabled(tag); +static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv* env) { + return tracing_perfetto::getEnabledCategories(); } static void android_os_Trace_nativeRegisterWithPerfetto(JNIEnv* env) { @@ -157,7 +157,7 @@ static const JNINativeMethod gTraceMethods[] = { {"nativeRegisterWithPerfetto", "()V", (void*)android_os_Trace_nativeRegisterWithPerfetto}, // ----------- @CriticalNative ---------------- - {"nativeIsTagEnabled", "(J)Z", (void*)android_os_Trace_nativeIsTagEnabled}, + {"nativeGetEnabledTags", "()J", (void*)android_os_Trace_nativeGetEnabledTags}, }; int register_android_os_Trace(JNIEnv* env) { diff --git a/data/keyboards/Vendor_054c_Product_05c4.idc b/data/keyboards/Vendor_054c_Product_05c4.idc index 2cb3f7b90fed..9576e8d042ba 100644 --- a/data/keyboards/Vendor_054c_Product_05c4.idc +++ b/data/keyboards/Vendor_054c_Product_05c4.idc @@ -13,9 +13,11 @@ # limitations under the License. # -# Sony DS4 motion sensor configuration file. +# Sony Playstation(R) DualShock 4 Controller # +## Motion sensor ## + # reporting mode 0 - continuous sensor.accelerometer.reportingMode = 0 # The delay between sensor events corresponding to the lowest frequency in microsecond @@ -33,3 +35,27 @@ sensor.gyroscope.maxDelay = 100000 sensor.gyroscope.minDelay = 5000 # The power in mA used by this sensor while in use sensor.gyroscope.power = 0.8 + +## Touchpad ## + +# After the DualShock 4 has been connected over Bluetooth for a minute or so, +# its reports start bunching up in time, meaning that we receive 2–4 reports +# within a millisecond followed by a >10ms wait until the next batch. +# +# This uneven timing causes the apparent speed of a finger (calculated using +# time deltas between received reports) to vary dramatically even if it's +# actually moving smoothly across the touchpad, triggering the touchpad stack's +# drumroll detection logic, which causes the finger's single smooth movement to +# be treated as many small movements of consecutive touches, which are then +# inhibited by the click wiggle filter. +# +# Since this touchpad does not seem vulnerable to click wiggle, we can safely +# disable drumroll detection due to speed changes (by setting the speed change +# threshold very high, since there's no boolean control property). +gestureProp.Drumroll_Max_Speed_Change_Factor = 1000000000 + +# Because of the way this touchpad is positioned, touches around the edges are +# no more likely to be palms than ones in the middle, so remove the edge zones +# from the palm classifier to increase the usable area of the pad. +gestureProp.Palm_Edge_Zone_Width = 0 +gestureProp.Tap_Exclusion_Border_Width = 0 diff --git a/data/keyboards/Vendor_054c_Product_09cc.idc b/data/keyboards/Vendor_054c_Product_09cc.idc index 2cb3f7b90fed..9576e8d042ba 100644 --- a/data/keyboards/Vendor_054c_Product_09cc.idc +++ b/data/keyboards/Vendor_054c_Product_09cc.idc @@ -13,9 +13,11 @@ # limitations under the License. # -# Sony DS4 motion sensor configuration file. +# Sony Playstation(R) DualShock 4 Controller # +## Motion sensor ## + # reporting mode 0 - continuous sensor.accelerometer.reportingMode = 0 # The delay between sensor events corresponding to the lowest frequency in microsecond @@ -33,3 +35,27 @@ sensor.gyroscope.maxDelay = 100000 sensor.gyroscope.minDelay = 5000 # The power in mA used by this sensor while in use sensor.gyroscope.power = 0.8 + +## Touchpad ## + +# After the DualShock 4 has been connected over Bluetooth for a minute or so, +# its reports start bunching up in time, meaning that we receive 2–4 reports +# within a millisecond followed by a >10ms wait until the next batch. +# +# This uneven timing causes the apparent speed of a finger (calculated using +# time deltas between received reports) to vary dramatically even if it's +# actually moving smoothly across the touchpad, triggering the touchpad stack's +# drumroll detection logic, which causes the finger's single smooth movement to +# be treated as many small movements of consecutive touches, which are then +# inhibited by the click wiggle filter. +# +# Since this touchpad does not seem vulnerable to click wiggle, we can safely +# disable drumroll detection due to speed changes (by setting the speed change +# threshold very high, since there's no boolean control property). +gestureProp.Drumroll_Max_Speed_Change_Factor = 1000000000 + +# Because of the way this touchpad is positioned, touches around the edges are +# no more likely to be palms than ones in the middle, so remove the edge zones +# from the palm classifier to increase the usable area of the pad. +gestureProp.Palm_Edge_Zone_Width = 0 +gestureProp.Tap_Exclusion_Border_Width = 0 diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 7aecfd8d4a0d..9ba5a81dbb71 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -880,9 +880,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** - * Returns {@code true} if the screen must be unlocked for this key to be used for decryption or - * signing. Encryption and signature verification will still be available when the screen is - * locked. + * Returns {@code true} if the key is authorized to be used only while the device is unlocked. * * @see Builder#setUnlockedDeviceRequired(boolean) */ @@ -1723,11 +1721,49 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** - * Sets whether the keystore requires the screen to be unlocked before allowing decryption - * using this key. If this is set to {@code true}, any attempt to decrypt or sign using this - * key while the screen is locked will fail. A locked device requires a PIN, password, - * biometric, or other trusted factor to access. While the screen is locked, any associated - * public key can still be used (e.g for signature verification). + * Sets whether this key is authorized to be used only while the device is unlocked. + * <p> + * The device is considered to be locked for a user when the user's apps are currently + * inaccessible and some form of lock screen authentication is required to regain access to + * them. For the full definition, see {@link KeyguardManager#isDeviceLocked()}. + * <p> + * Public key operations aren't restricted by {@code setUnlockedDeviceRequired(true)} and + * may be performed even while the device is locked. In Android 11 (API level 30) and lower, + * encryption and verification operations with symmetric keys weren't restricted either. + * <p> + * Keys that use {@code setUnlockedDeviceRequired(true)} can be imported and generated even + * while the device is locked, as long as the device has been unlocked at least once since + * the last reboot. However, such keys cannot be used (except for the unrestricted + * operations mentioned above) until the device is unlocked. Apps that need to encrypt data + * while the device is locked such that it can only be decrypted while the device is + * unlocked can generate a key and encrypt the data in software, import the key into + * Keystore using {@code setUnlockedDeviceRequired(true)}, and zeroize the original key. + * <p> + * {@code setUnlockedDeviceRequired(true)} is related to but distinct from + * {@link #setUserAuthenticationRequired(boolean) setUserAuthenticationRequired(true)}. + * {@code setUnlockedDeviceRequired(true)} requires that the device be unlocked, whereas + * {@code setUserAuthenticationRequired(true)} requires that a specific type of strong + * authentication has happened within a specific time period. They may be used together or + * separately; there are cases in which one requirement can be satisfied but not the other. + * <p> + * <b>Warning:</b> Be careful using {@code setUnlockedDeviceRequired(true)} on Android 14 + * (API level 34) and lower, since the following bugs existed in Android 12 through 14: + * <ul> + * <li>When the user didn't have a secure lock screen, unlocked-device-required keys + * couldn't be generated, imported, or used.</li> + * <li>When the user's secure lock screen was removed, all of that user's + * unlocked-device-required keys were automatically deleted.</li> + * <li>Unlocking the device with a non-strong biometric, such as face on many devices, + * didn't re-authorize the use of unlocked-device-required keys.</li> + * <li>Unlocking the device with a biometric didn't re-authorize the use of + * unlocked-device-required keys in profiles that share their parent user's lock.</li> + * </ul> + * These issues are fixed in Android 15, so apps can avoid them by using + * {@code setUnlockedDeviceRequired(true)} only on Android 15 and higher. + * Apps that use both {@code setUnlockedDeviceRequired(true)} and + * {@link #setUserAuthenticationRequired(boolean) setUserAuthenticationRequired(true)} + * are unaffected by the first two issues, since the first two issues describe expected + * behavior for {@code setUserAuthenticationRequired(true)}. */ @NonNull public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java index 5cffe46936a2..2163ca2f8217 100644 --- a/keystore/java/android/security/keystore/KeyInfo.java +++ b/keystore/java/android/security/keystore/KeyInfo.java @@ -279,7 +279,7 @@ public class KeyInfo implements KeySpec { } /** - * Returns {@code true} if the key is authorized to be used only when the device is unlocked. + * Returns {@code true} if the key is authorized to be used only while the device is unlocked. * * <p>This authorization applies only to secret key and private key operations. Public key * operations are not restricted. diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index 31b4a5eac619..9b455f05b99c 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -577,9 +577,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** - * Returns {@code true} if the screen must be unlocked for this key to be used for decryption or - * signing. Encryption and signature verification will still be available when the screen is - * locked. + * Returns {@code true} if the key is authorized to be used only while the device is unlocked. * * @see Builder#setUnlockedDeviceRequired(boolean) */ @@ -1117,11 +1115,49 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** - * Sets whether the keystore requires the screen to be unlocked before allowing decryption - * using this key. If this is set to {@code true}, any attempt to decrypt or sign using this - * key while the screen is locked will fail. A locked device requires a PIN, password, - * biometric, or other trusted factor to access. While the screen is locked, the key can - * still be used for encryption or signature verification. + * Sets whether this key is authorized to be used only while the device is unlocked. + * <p> + * The device is considered to be locked for a user when the user's apps are currently + * inaccessible and some form of lock screen authentication is required to regain access to + * them. For the full definition, see {@link KeyguardManager#isDeviceLocked()}. + * <p> + * Public key operations aren't restricted by {@code setUnlockedDeviceRequired(true)} and + * may be performed even while the device is locked. In Android 11 (API level 30) and lower, + * encryption and verification operations with symmetric keys weren't restricted either. + * <p> + * Keys that use {@code setUnlockedDeviceRequired(true)} can be imported and generated even + * while the device is locked, as long as the device has been unlocked at least once since + * the last reboot. However, such keys cannot be used (except for the unrestricted + * operations mentioned above) until the device is unlocked. Apps that need to encrypt data + * while the device is locked such that it can only be decrypted while the device is + * unlocked can generate a key and encrypt the data in software, import the key into + * Keystore using {@code setUnlockedDeviceRequired(true)}, and zeroize the original key. + * <p> + * {@code setUnlockedDeviceRequired(true)} is related to but distinct from + * {@link #setUserAuthenticationRequired(boolean) setUserAuthenticationRequired(true)}. + * {@code setUnlockedDeviceRequired(true)} requires that the device be unlocked, whereas + * {@code setUserAuthenticationRequired(true)} requires that a specific type of strong + * authentication has happened within a specific time period. They may be used together or + * separately; there are cases in which one requirement can be satisfied but not the other. + * <p> + * <b>Warning:</b> Be careful using {@code setUnlockedDeviceRequired(true)} on Android 14 + * (API level 34) and lower, since the following bugs existed in Android 12 through 14: + * <ul> + * <li>When the user didn't have a secure lock screen, unlocked-device-required keys + * couldn't be generated, imported, or used.</li> + * <li>When the user's secure lock screen was removed, all of that user's + * unlocked-device-required keys were automatically deleted.</li> + * <li>Unlocking the device with a non-strong biometric, such as face on many devices, + * didn't re-authorize the use of unlocked-device-required keys.</li> + * <li>Unlocking the device with a biometric didn't re-authorize the use of + * unlocked-device-required keys in profiles that share their parent user's lock.</li> + * </ul> + * These issues are fixed in Android 15, so apps can avoid them by using + * {@code setUnlockedDeviceRequired(true)} only on Android 15 and higher. + * Apps that use both {@code setUnlockedDeviceRequired(true)} and + * {@link #setUserAuthenticationRequired(boolean) setUserAuthenticationRequired(true)} + * are unaffected by the first two issues, since the first two issues describe expected + * behavior for {@code setUserAuthenticationRequired(true)}. */ @NonNull public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { diff --git a/nfc/Android.bp b/nfc/Android.bp index ca10949ec77b..7698e2b2d054 100644 --- a/nfc/Android.bp +++ b/nfc/Android.bp @@ -50,7 +50,7 @@ java_sdk_library { ], defaults: ["framework-module-defaults"], sdk_version: "module_current", - min_sdk_version: "VanillaIceCream", + min_sdk_version: "34", // should be 35 (making it 34 for compiling for `-next`) installable: true, optimize: { enabled: false, diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffect.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffect.kt index abe1e3de8eea..1c763e8c6108 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffect.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffect.kt @@ -181,6 +181,11 @@ private constructor( turbulenceNoiseShader.setColor(newColor) } + /** Updates the noise color that's screen blended on top. */ + fun updateScreenColor(newColor: Int) { + turbulenceNoiseShader.setScreenColor(newColor) + } + /** * Retrieves the noise offset x, y, z values. This is useful for replaying the animation * smoothly from the last animation, by passing in the last values to the next animation. @@ -322,7 +327,10 @@ private constructor( private fun draw() { paintCallback?.onDraw(paint!!) renderEffectCallback?.onDraw( - RenderEffect.createRuntimeShaderEffect(turbulenceNoiseShader, "in_src") + RenderEffect.createRuntimeShaderEffect( + turbulenceNoiseShader, + TurbulenceNoiseShader.BACKGROUND_UNIFORM + ) ) } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt index 59354c843447..ba8f1ace0214 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt @@ -52,7 +52,7 @@ data class TurbulenceNoiseAnimationConfig( /** Color of the effect. */ val color: Int = DEFAULT_COLOR, /** Background color of the effect. */ - val backgroundColor: Int = DEFAULT_BACKGROUND_COLOR, + val screenColor: Int = DEFAULT_SCREEN_COLOR, val width: Float = 0f, val height: Float = 0f, val maxDuration: Float = DEFAULT_MAX_DURATION_IN_MILLIS, @@ -72,7 +72,7 @@ data class TurbulenceNoiseAnimationConfig( */ val lumaMatteOverallBrightness: Float = DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS, /** Whether to flip the luma mask. */ - val shouldInverseNoiseLuminosity: Boolean = false + val shouldInverseNoiseLuminosity: Boolean = false, ) { companion object { const val DEFAULT_MAX_DURATION_IN_MILLIS = 30_000f // Max 30 sec @@ -83,7 +83,7 @@ data class TurbulenceNoiseAnimationConfig( const val DEFAULT_COLOR = Color.WHITE const val DEFAULT_LUMA_MATTE_BLEND_FACTOR = 1f const val DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS = 0f - const val DEFAULT_BACKGROUND_COLOR = Color.BLACK + const val DEFAULT_SCREEN_COLOR = Color.BLACK private val random = Random() } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt index 8dd90a8ffe9f..025c8b9dce04 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt @@ -16,6 +16,7 @@ package com.android.systemui.surfaceeffects.turbulencenoise import android.graphics.RuntimeShader +import com.android.systemui.surfaceeffects.shaders.SolidColorShader import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary import java.lang.Float.max @@ -28,9 +29,11 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) : RuntimeShader(getShader(baseType)) { // language=AGSL companion object { + /** Uniform name for the background buffer (e.g. image, solid color, etc.). */ + const val BACKGROUND_UNIFORM = "in_src" private const val UNIFORMS = """ - uniform shader in_src; // Needed to support RenderEffect. + uniform shader ${BACKGROUND_UNIFORM}; uniform float in_gridNum; uniform vec3 in_noiseMove; uniform vec2 in_size; @@ -41,7 +44,7 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) : uniform half in_lumaMatteBlendFactor; uniform half in_lumaMatteOverallBrightness; layout(color) uniform vec4 in_color; - layout(color) uniform vec4 in_backgroundColor; + layout(color) uniform vec4 in_screenColor; """ private const val SIMPLEX_SHADER = @@ -50,22 +53,20 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) : vec2 uv = p / in_size.xy; uv.x *= in_aspectRatio; + // Compute turbulence effect with the uv distorted with simplex noise. vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - // Bring it to [0, 1] range. - float luma = (simplex3d(noiseP) * in_inverseLuma) * 0.5 + 0.5; - luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness) - * in_opacity; - vec3 mask = maskLuminosity(in_color.rgb, luma); - vec3 color = in_backgroundColor.rgb + mask * 0.6; + vec3 color = getColorTurbulenceMask(simplex3d(noiseP) * in_inverseLuma); + + // Blend the result with the background color. + color = in_src.eval(p).rgb + color * 0.6; // Add dither with triangle distribution to avoid color banding. Dither in the // shader here as we are in gamma space. float dither = triangleNoise(p * in_pixelDensity) / 255.; + color += dither.rrr; - // The result color should be pre-multiplied, i.e. [R*A, G*A, B*A, A], thus need to - // multiply rgb with a to get the correct result. - color = (color + dither.rrr) * in_opacity; - return vec4(color, in_opacity); + // Return the pre-multiplied alpha result, i.e. [R*A, G*A, B*A, A]. + return vec4(color * in_opacity, in_opacity); } """ @@ -76,32 +77,105 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) : uv.x *= in_aspectRatio; vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - // Bring it to [0, 1] range. - float luma = (simplex3d_fractal(noiseP) * in_inverseLuma) * 0.5 + 0.5; - luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness) - * in_opacity; - vec3 mask = maskLuminosity(in_color.rgb, luma); - vec3 color = in_backgroundColor.rgb + mask * 0.6; + vec3 color = getColorTurbulenceMask(simplex3d_fractal(noiseP) * in_inverseLuma); + + // Blend the result with the background color. + color = in_src.eval(p).rgb + color * 0.6; // Skip dithering. return vec4(color * in_opacity, in_opacity); } """ + + /** + * This effect has two layers: color turbulence effect with sparkles on top. + * 1. Gets the luma matte using Simplex noise. + * 2. Generate a colored turbulence layer with the luma matte. + * 3. Generate a colored sparkle layer with the same luma matter. + * 4. Apply a screen color to the background image. + * 5. Composite the previous result with the color turbulence. + * 6. Composite the latest result with the sparkles. + */ + private const val SIMPLEX_SPARKLE_SHADER = + """ + vec4 main(vec2 p) { + vec2 uv = p / in_size.xy; + uv.x *= in_aspectRatio; + + vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; + // Luma is used for both color and sparkle masks. + float luma = simplex3d(noiseP) * in_inverseLuma; + + // Get color layer (color mask with in_color applied) + vec3 colorLayer = getColorTurbulenceMask(simplex3d(noiseP) * in_inverseLuma); + float dither = triangleNoise(p * in_pixelDensity) / 255.; + colorLayer += dither.rrr; + + // Get sparkle layer (sparkle mask with particles & in_color applied) + vec3 sparkleLayer = getSparkleTurbulenceMask(luma, p); + + // Composite with the background. + half4 bgColor = in_src.eval(p); + half sparkleOpacity = smoothstep(0, 0.75, in_opacity); + + half3 effect = screen(bgColor.rgb, in_screenColor.rgb); + effect = screen(effect, colorLayer * 0.22); + effect += sparkleLayer * sparkleOpacity; + + return mix(bgColor, vec4(effect, 1.), in_opacity); + } + """ + + private const val COMMON_FUNCTIONS = + /** + * Below two functions generate turbulence layers (color or sparkles applied) with the + * given luma matte. They both return a mask with in_color applied. + */ + """ + vec3 getColorTurbulenceMask(float luma) { + // Bring it to [0, 1] range. + luma = luma * 0.5 + 0.5; + + half colorLuma = + saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness) + * in_opacity; + vec3 colorLayer = maskLuminosity(in_color.rgb, colorLuma); + + return colorLayer; + } + + vec3 getSparkleTurbulenceMask(float luma, vec2 p) { + half lumaIntensity = 1.75; + half lumaBrightness = -1.3; + half sparkleLuma = max(luma * lumaIntensity + lumaBrightness, 0.); + + float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_noiseMove.z); + vec3 sparkleLayer = maskLuminosity(in_color.rgb * sparkle, sparkleLuma); + + return sparkleLayer; + } + """ private const val SIMPLEX_NOISE_SHADER = - ShaderUtilLibrary.SHADER_LIB + UNIFORMS + SIMPLEX_SHADER + ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + SIMPLEX_SHADER private const val FRACTAL_NOISE_SHADER = - ShaderUtilLibrary.SHADER_LIB + UNIFORMS + FRACTAL_SHADER - // TODO (b/282007590): Add NOISE_WITH_SPARKLE + ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + FRACTAL_SHADER + private const val SPARKLE_NOISE_SHADER = + ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + SIMPLEX_SPARKLE_SHADER enum class Type { + /** Effect with a simple color noise turbulence. */ SIMPLEX_NOISE, + /** Effect with a simple color noise turbulence, with fractal. */ SIMPLEX_NOISE_FRACTAL, + /** Effect with color & sparkle turbulence with screen color layer. */ + SIMPLEX_NOISE_SPARKLE } fun getShader(type: Type): String { return when (type) { Type.SIMPLEX_NOISE -> SIMPLEX_NOISE_SHADER Type.SIMPLEX_NOISE_FRACTAL -> FRACTAL_NOISE_SHADER + Type.SIMPLEX_NOISE_SPARKLE -> SPARKLE_NOISE_SHADER } } } @@ -111,7 +185,7 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) : setGridCount(config.gridCount) setPixelDensity(config.pixelDensity) setColor(config.color) - setBackgroundColor(config.backgroundColor) + setScreenColor(config.screenColor) setSize(config.width, config.height) setLumaMatteFactors(config.lumaMatteBlendFactor, config.lumaMatteOverallBrightness) setInverseNoiseLuminosity(config.shouldInverseNoiseLuminosity) @@ -137,9 +211,20 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) : setColorUniform("in_color", color) } - /** Sets the background color of the effect. Alpha is ignored. */ + /** + * Sets the color that is used for blending on top of the background color/image. Only relevant + * to [Type.SIMPLEX_NOISE_SPARKLE]. + */ + fun setScreenColor(color: Int) { + setColorUniform("in_screenColor", color) + } + + /** + * Sets the background color of the effect. Alpha is ignored. If you are using [RenderEffect], + * no need to call this function since the background image of the View will be used. + */ fun setBackgroundColor(color: Int) { - setColorUniform("in_backgroundColor", color) + setInputShader(BACKGROUND_UNIFORM, SolidColorShader(color)) } /** @@ -163,7 +248,7 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) : * * @param lumaMatteBlendFactor increases or decreases the amount of variance in noise. Setting * this a lower number removes variations. I.e. the turbulence noise will look more blended. - * Expected input range is [0, 1]. more dimmed. + * Expected input range is [0, 1]. * @param lumaMatteOverallBrightness adds the overall brightness of the turbulence noise. * Expected input range is [0, 1]. * diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java index a8999ff31f8a..6c8949e51094 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java @@ -16,6 +16,7 @@ package com.android.systemui.plugins.qs; import android.content.Context; import android.view.View; +import android.view.ViewConfiguration; import android.widget.LinearLayout; import com.android.systemui.plugins.annotations.DependsOn; @@ -74,4 +75,9 @@ public abstract class QSTileView extends LinearLayout { /** Sets the index of this tile in its layout */ public abstract void setPosition(int position); + + /** Get the duration of a visuo-haptic long-press effect */ + public int getLongPressEffectDuration() { + return ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index ac99fc69b2b5..85f63e9f1974 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -126,6 +126,7 @@ constructor( field?.let { oldView -> val lottie = oldView.requireViewById(R.id.sidefps_animation) as LottieAnimationView lottie.pauseAnimation() + lottie.removeAllLottieOnCompositionLoadedListener() windowManager.removeView(oldView) orientationListener.disable() } @@ -288,7 +289,7 @@ constructor( } private fun onOrientationChanged(@BiometricRequestConstants.RequestReason reason: Int) { - if (overlayView != null) { + if (overlayView?.isAttachedToWindow == true) { createOverlayForDisplay(reason) } } @@ -322,7 +323,7 @@ constructor( ) lottie.addLottieOnCompositionLoadedListener { // Check that view is not stale, and that overlayView has not been hidden/removed - if (overlayView != null && overlayView == view) { + if (overlayView?.isAttachedToWindow == true && overlayView == view) { updateOverlayParams(display, it.bounds) } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt index ed1557cccd01..c4967ec0df21 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.biometrics.shared.model.AuthenticationReason.Setting import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map /** Encapsulates business logic for interacting with biometric authentication state. */ @@ -52,7 +53,7 @@ constructor( } else { AuthenticationReason.NotRunning } - } + }.distinctUntilChanged() override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> = biometricStatusRepository.fingerprintAcquiredStatus diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt index ec72a1422973..f1620d96b159 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt @@ -214,6 +214,24 @@ class QSLongPressEffect( _actionType.value = null } + /** + * Reset the effect with a new effect duration. + * + * The effect will go back to an [IDLE] state where it can begin its logic with a new duration. + * + * @param[duration] New duration for the long-press effect + */ + fun resetWithDuration(duration: Int) { + // The effect can't reset if it is running + if (effectAnimator.isRunning) return + + effectAnimator.duration = duration.toLong() + _effectProgress.value = 0f + _actionType.value = null + waitJob?.cancel() + state = State.IDLE + } + enum class State { IDLE, /* The effect is idle waiting for touch input */ TIMEOUT_WAIT, /* The effect is waiting for a [PRESSED_TIMEOUT] period */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt index b8ba09801ee8..5de1a61d61b5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt @@ -17,10 +17,10 @@ package com.android.systemui.keyguard.ui import android.view.animation.Interpolator import com.android.app.animation.Interpolators.LINEAR -import com.android.app.tracing.coroutines.launch import com.android.keyguard.logging.KeyguardTransitionAnimationLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState @@ -35,6 +35,7 @@ import kotlin.math.max import kotlin.math.min import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow @@ -42,6 +43,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.launch /** * Assists in creating sub-flows for a KeyguardTransition. Call [setup] once for a transition, and @@ -52,13 +54,14 @@ class KeyguardTransitionAnimationFlow @Inject constructor( @Application private val scope: CoroutineScope, + @Main private val mainDispatcher: CoroutineDispatcher, private val transitionInteractor: KeyguardTransitionInteractor, private val logger: KeyguardTransitionAnimationLogger, ) { private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>() init { - scope.launch("KeyguardTransitionAnimationFlow") { + scope.launch(mainDispatcher) { transitionInteractor.transitions.collect { // FROM->TO transitionMap[Edge(it.from, it.to)]?.emit(it) diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt index 5f7991e62cd7..1c11178b5b35 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt @@ -24,12 +24,13 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.lifecycleScope +import com.android.app.tracing.coroutines.createCoroutineTracingContext +import com.android.app.tracing.coroutines.launch import com.android.systemui.util.Assert import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch /** * Runs the given [block] every time the [View] becomes attached (or immediately after calling this @@ -66,7 +67,8 @@ fun View.repeatWhenAttached( // dispatcher to use. We don't want it to run on the Dispatchers.Default thread pool as // default behavior. Instead, we want it to run on the view's UI thread since the user will // presumably want to call view methods that require being called from said UI thread. - val lifecycleCoroutineContext = Dispatchers.Main + coroutineContext + val lifecycleCoroutineContext = + Dispatchers.Main + createCoroutineTracingContext() + coroutineContext var lifecycleOwner: ViewLifecycleOwner? = null val onAttachListener = object : View.OnAttachStateChangeListener { @@ -97,14 +99,12 @@ fun View.repeatWhenAttached( ) } - return object : DisposableHandle { - override fun dispose() { - Assert.isMainThread() + return DisposableHandle { + Assert.isMainThread() - lifecycleOwner?.onDestroy() - lifecycleOwner = null - view.removeOnAttachStateChangeListener(onAttachListener) - } + lifecycleOwner?.onDestroy() + lifecycleOwner = null + view.removeOnAttachStateChangeListener(onAttachListener) } } @@ -115,7 +115,12 @@ private fun createLifecycleOwnerAndRun( ): ViewLifecycleOwner { return ViewLifecycleOwner(view).apply { onCreate() - lifecycleScope.launch(coroutineContext) { block(view) } + lifecycleScope.launch( + "ViewLifecycleOwner(${view::class.java.simpleName})", + coroutineContext + ) { + block(view) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java index 26c63f31fa46..899b9ed103cd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java @@ -1306,7 +1306,7 @@ public class MediaControlPanel { TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z, // Color will be correctly updated in ColorSchemeTransition. /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(), - /* backgroundColor= */ Color.BLACK, + /* screenColor= */ Color.BLACK, width, height, TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 63963ded2923..e1c543f8f025 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -37,7 +37,6 @@ import android.util.TypedValue import android.view.Gravity import android.view.LayoutInflater import android.view.View -import android.view.ViewConfiguration import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo @@ -185,6 +184,8 @@ open class QSTileViewImpl @JvmOverloads constructor( private var initialLongPressProperties: QSLongPressProperties? = null private var finalLongPressProperties: QSLongPressProperties? = null private val colorEvaluator = ArgbEvaluator.getInstance() + val hasLongPressEffect: Boolean + get() = longPressEffect != null init { val typedValue = TypedValue() @@ -611,10 +612,9 @@ open class QSTileViewImpl @JvmOverloads constructor( // Long-press effects if (quickSettingsVisualHapticsLongpress()){ - if (state.handlesLongClick) { - // initialize the long-press effect and set it as the touch listener + if (state.handlesLongClick && maybeCreateAndInitializeLongPressEffect()) { + // set the valid long-press effect as the touch listener showRippleEffect = false - initializeLongPressEffect() setOnTouchListener(longPressEffect) QSLongPressEffectViewBinder.bind(this, longPressEffect) } else { @@ -751,7 +751,7 @@ open class QSTileViewImpl @JvmOverloads constructor( override fun onActivityLaunchAnimationEnd() = resetLongPressEffectProperties() fun updateLongPressEffectProperties(effectProgress: Float) { - if (!isLongClickable) return + if (!isLongClickable || longPressEffect == null) return setAllColors( colorEvaluator.evaluate( effectProgress, @@ -836,13 +836,25 @@ open class QSTileViewImpl @JvmOverloads constructor( icon.setTint(icon.mIcon as ImageView, lastIconTint) } - private fun initializeLongPressEffect() { + private fun maybeCreateAndInitializeLongPressEffect(): Boolean { + // Don't setup the effect if the long-press duration is invalid + val effectDuration = longPressEffectDuration + if (effectDuration <= 0) { + longPressEffect = null + return false + } + initializeLongPressProperties() - longPressEffect = - QSLongPressEffect( - vibratorHelper, - ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout(), - ) + if (longPressEffect == null) { + longPressEffect = + QSLongPressEffect( + vibratorHelper, + effectDuration, + ) + } else { + longPressEffect?.resetWithDuration(effectDuration) + } + return true } private fun initializeLongPressProperties() { diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt index ac1d2803835a..e977014e00f2 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt @@ -91,7 +91,8 @@ constructor( ) controller.init() - applicationScope.launch(bgHandler.asCoroutineDispatcher()) { + val bgDispatcher = bgHandler.asCoroutineDispatcher("@UnfoldBg Handler") + applicationScope.launch(bgDispatcher) { powerInteractor.screenPowerState.collect { if (it == ScreenPowerState.SCREEN_ON) { readyCallback = null @@ -99,7 +100,7 @@ constructor( } } - applicationScope.launch(bgHandler.asCoroutineDispatcher()) { + applicationScope.launch(bgDispatcher) { deviceStateRepository.state .map { it == DeviceStateRepository.DeviceState.FOLDED } .distinctUntilChanged() diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt index 66fdf538e284..933ddb5739e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt @@ -16,25 +16,22 @@ package com.android.systemui.haptics.slider -import android.os.VibrationAttributes import android.os.VibrationEffect import android.view.VelocityTracker import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.statusbar.VibratorHelper -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq +import com.android.systemui.haptics.vibratorHelper +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.whenever -import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.util.time.fakeSystemClock import kotlin.math.max import kotlin.test.assertEquals +import kotlin.test.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.times -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @@ -42,10 +39,10 @@ import org.mockito.MockitoAnnotations class SliderHapticFeedbackProviderTest : SysuiTestCase() { @Mock private lateinit var velocityTracker: VelocityTracker - @Mock private lateinit var vibratorHelper: VibratorHelper + + private val kosmos = testKosmos() private val config = SliderHapticFeedbackConfig() - private val clock = FakeSystemClock() private val lowTickDuration = 12 // Mocked duration of a low tick private val dragTextureThresholdMillis = @@ -55,250 +52,278 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - whenever(vibratorHelper.getPrimitiveDurations(any())) - .thenReturn(intArrayOf(lowTickDuration)) whenever(velocityTracker.isAxisSupported(config.velocityAxis)).thenReturn(true) whenever(velocityTracker.getAxisVelocity(config.velocityAxis)) .thenReturn(config.maxVelocityToScale) + + kosmos.vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_LOW_TICK] = + lowTickDuration sliderHapticFeedbackProvider = - SliderHapticFeedbackProvider(vibratorHelper, velocityTracker, config, clock) + SliderHapticFeedbackProvider( + kosmos.vibratorHelper, + velocityTracker, + config, + kosmos.fakeSystemClock, + ) } @Test - fun playHapticAtLowerBookend_playsClick() { - val vibration = - VibrationEffect.startComposition() - .addPrimitive( - VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), - ) - .compose() - - sliderHapticFeedbackProvider.onLowerBookend() - - verify(vibratorHelper).vibrate(eq(vibration), any(VibrationAttributes::class.java)) - } + fun playHapticAtLowerBookend_playsClick() = + with(kosmos) { + val vibration = + VibrationEffect.startComposition() + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, + sliderHapticFeedbackProvider.scaleOnEdgeCollision( + config.maxVelocityToScale + ), + ) + .compose() + + sliderHapticFeedbackProvider.onLowerBookend() + + assertTrue(vibratorHelper.hasVibratedWithEffects(vibration)) + } @Test - fun playHapticAtLowerBookend_twoTimes_playsClickOnlyOnce() { - val vibration = - VibrationEffect.startComposition() - .addPrimitive( - VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale) - ) - .compose() - - sliderHapticFeedbackProvider.onLowerBookend() - sliderHapticFeedbackProvider.onLowerBookend() - - verify(vibratorHelper).vibrate(eq(vibration), any(VibrationAttributes::class.java)) - } + fun playHapticAtLowerBookend_twoTimes_playsClickOnlyOnce() = + with(kosmos) { + val vibration = + VibrationEffect.startComposition() + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale) + ) + .compose() + + sliderHapticFeedbackProvider.onLowerBookend() + sliderHapticFeedbackProvider.onLowerBookend() + + assertTrue(vibratorHelper.hasVibratedWithEffects(vibration)) + } @Test - fun playHapticAtUpperBookend_playsClick() { - val vibration = - VibrationEffect.startComposition() - .addPrimitive( - VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), - ) - .compose() - - sliderHapticFeedbackProvider.onUpperBookend() + fun playHapticAtUpperBookend_playsClick() = + with(kosmos) { + val vibration = + VibrationEffect.startComposition() + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, + sliderHapticFeedbackProvider.scaleOnEdgeCollision( + config.maxVelocityToScale + ), + ) + .compose() + + sliderHapticFeedbackProvider.onUpperBookend() + + assertTrue(vibratorHelper.hasVibratedWithEffects(vibration)) + } - verify(vibratorHelper).vibrate(eq(vibration), any(VibrationAttributes::class.java)) - } + @Test + fun playHapticAtUpperBookend_twoTimes_playsClickOnlyOnce() = + with(kosmos) { + val vibration = + VibrationEffect.startComposition() + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, + sliderHapticFeedbackProvider.scaleOnEdgeCollision( + config.maxVelocityToScale + ), + ) + .compose() + + sliderHapticFeedbackProvider.onUpperBookend() + sliderHapticFeedbackProvider.onUpperBookend() + + assertEquals(/* expected=*/ 1, vibratorHelper.timesVibratedWithEffect(vibration)) + } @Test - fun playHapticAtUpperBookend_twoTimes_playsClickOnlyOnce() { - val vibration = - VibrationEffect.startComposition() - .addPrimitive( - VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), + fun playHapticAtProgress_onQuickSuccession_playsLowTicksOnce() = + with(kosmos) { + // GIVEN max velocity and slider progress + val progress = 1f + val expectedScale = + sliderHapticFeedbackProvider.scaleOnDragTexture( + config.maxVelocityToScale, + progress, ) - .compose() + val ticks = VibrationEffect.startComposition() + repeat(config.numberOfLowTicks) { + ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) + } - sliderHapticFeedbackProvider.onUpperBookend() - sliderHapticFeedbackProvider.onUpperBookend() + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) - verify(vibratorHelper, times(1)) - .vibrate(eq(vibration), any(VibrationAttributes::class.java)) - } + // WHEN two calls to play occur immediately + sliderHapticFeedbackProvider.onProgress(progress) + sliderHapticFeedbackProvider.onProgress(progress) - @Test - fun playHapticAtProgress_onQuickSuccession_playsLowTicksOnce() { - // GIVEN max velocity and slider progress - val progress = 1f - val expectedScale = - sliderHapticFeedbackProvider.scaleOnDragTexture( - config.maxVelocityToScale, - progress, - ) - val ticks = VibrationEffect.startComposition() - repeat(config.numberOfLowTicks) { - ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) + // THEN the correct composition only plays once + assertEquals(/* expected=*/ 1, vibratorHelper.timesVibratedWithEffect(ticks.compose())) } - // GIVEN system running for 1s - clock.advanceTime(1000) - - // WHEN two calls to play occur immediately - sliderHapticFeedbackProvider.onProgress(progress) - sliderHapticFeedbackProvider.onProgress(progress) - - // THEN the correct composition only plays once - verify(vibratorHelper, times(1)) - .vibrate(eq(ticks.compose()), any(VibrationAttributes::class.java)) - } - @Test - fun playHapticAtProgress_beforeNextDragThreshold_playsLowTicksOnce() { - // GIVEN max velocity and a slider progress at half progress - val firstProgress = 0.5f - val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress) - - // Given a second slider progress event smaller than the progress threshold - val secondProgress = firstProgress + max(0f, config.deltaProgressForDragThreshold - 0.01f) - - // GIVEN system running for 1s - clock.advanceTime(1000) - - // WHEN two calls to play occur with the required threshold separation (time and progress) - sliderHapticFeedbackProvider.onProgress(firstProgress) - clock.advanceTime(dragTextureThresholdMillis.toLong()) - sliderHapticFeedbackProvider.onProgress(secondProgress) - - // THEN Only the first compositions plays - verify(vibratorHelper, times(1)) - .vibrate(eq(firstTicks), any(VibrationAttributes::class.java)) - verify(vibratorHelper, times(1)) - .vibrate(any(VibrationEffect::class.java), any(VibrationAttributes::class.java)) - } + fun playHapticAtProgress_beforeNextDragThreshold_playsLowTicksOnce() = + with(kosmos) { + // GIVEN max velocity and a slider progress at half progress + val firstProgress = 0.5f + val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress) + + // Given a second slider progress event smaller than the progress threshold + val secondProgress = + firstProgress + max(0f, config.deltaProgressForDragThreshold - 0.01f) + + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) + + // WHEN two calls to play occur with the required threshold separation (time and + // progress) + sliderHapticFeedbackProvider.onProgress(firstProgress) + fakeSystemClock.advanceTime(dragTextureThresholdMillis.toLong()) + sliderHapticFeedbackProvider.onProgress(secondProgress) + + // THEN Only the first compositions plays + assertEquals(/* expected= */ 1, vibratorHelper.timesVibratedWithEffect(firstTicks)) + assertEquals(/* expected= */ 1, vibratorHelper.totalVibrations) + } @Test - fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() { - // GIVEN max velocity and a slider progress at half progress - val firstProgress = 0.5f - val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress) - - // Given a second slider progress event beyond progress threshold - val secondProgress = firstProgress + config.deltaProgressForDragThreshold + 0.01f - val secondTicks = generateTicksComposition(config.maxVelocityToScale, secondProgress) - - // GIVEN system running for 1s - clock.advanceTime(1000) - - // WHEN two calls to play occur with the required threshold separation (time and progress) - sliderHapticFeedbackProvider.onProgress(firstProgress) - clock.advanceTime(dragTextureThresholdMillis.toLong()) - sliderHapticFeedbackProvider.onProgress(secondProgress) - - // THEN the correct compositions play - verify(vibratorHelper, times(1)) - .vibrate(eq(firstTicks), any(VibrationAttributes::class.java)) - verify(vibratorHelper, times(1)) - .vibrate(eq(secondTicks), any(VibrationAttributes::class.java)) - } + fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() = + with(kosmos) { + // GIVEN max velocity and a slider progress at half progress + val firstProgress = 0.5f + val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress) + + // Given a second slider progress event beyond progress threshold + val secondProgress = firstProgress + config.deltaProgressForDragThreshold + 0.01f + val secondTicks = generateTicksComposition(config.maxVelocityToScale, secondProgress) + + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) + + // WHEN two calls to play occur with the required threshold separation (time and + // progress) + sliderHapticFeedbackProvider.onProgress(firstProgress) + fakeSystemClock.advanceTime(dragTextureThresholdMillis.toLong()) + sliderHapticFeedbackProvider.onProgress(secondProgress) + + // THEN the correct compositions play + assertEquals(/* expected= */ 1, vibratorHelper.timesVibratedWithEffect(firstTicks)) + assertEquals(/* expected= */ 1, vibratorHelper.timesVibratedWithEffect(secondTicks)) + } @Test - fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTwice() { - // GIVEN max velocity and slider progress - val progress = 1f - val expectedScale = - sliderHapticFeedbackProvider.scaleOnDragTexture( - config.maxVelocityToScale, - progress, + fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTwice() = + with(kosmos) { + // GIVEN max velocity and slider progress + val progress = 1f + val expectedScale = + sliderHapticFeedbackProvider.scaleOnDragTexture( + config.maxVelocityToScale, + progress, + ) + val ticks = VibrationEffect.startComposition() + repeat(config.numberOfLowTicks) { + ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) + } + val bookendVibration = + VibrationEffect.startComposition() + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, + sliderHapticFeedbackProvider.scaleOnEdgeCollision( + config.maxVelocityToScale + ), + ) + .compose() + + // GIVEN a vibration at the lower bookend followed by a request to vibrate at progress + sliderHapticFeedbackProvider.onLowerBookend() + sliderHapticFeedbackProvider.onProgress(progress) + + // WHEN a vibration is to trigger again at the lower bookend + sliderHapticFeedbackProvider.onLowerBookend() + + // THEN there are two bookend vibrations + assertEquals( + /* expected= */ 2, + vibratorHelper.timesVibratedWithEffect(bookendVibration) ) - val ticks = VibrationEffect.startComposition() - repeat(config.numberOfLowTicks) { - ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) } - val bookendVibration = - VibrationEffect.startComposition() - .addPrimitive( - VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), - ) - .compose() - - // GIVEN a vibration at the lower bookend followed by a request to vibrate at progress - sliderHapticFeedbackProvider.onLowerBookend() - sliderHapticFeedbackProvider.onProgress(progress) - - // WHEN a vibration is to trigger again at the lower bookend - sliderHapticFeedbackProvider.onLowerBookend() - - // THEN there are two bookend vibrations - verify(vibratorHelper, times(2)) - .vibrate(eq(bookendVibration), any(VibrationAttributes::class.java)) - } @Test - fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTwice() { - // GIVEN max velocity and slider progress - val progress = 1f - val expectedScale = - sliderHapticFeedbackProvider.scaleOnDragTexture( - config.maxVelocityToScale, - progress, + fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTwice() = + with(kosmos) { + // GIVEN max velocity and slider progress + val progress = 1f + val expectedScale = + sliderHapticFeedbackProvider.scaleOnDragTexture( + config.maxVelocityToScale, + progress, + ) + val ticks = VibrationEffect.startComposition() + repeat(config.numberOfLowTicks) { + ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) + } + val bookendVibration = + VibrationEffect.startComposition() + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, + sliderHapticFeedbackProvider.scaleOnEdgeCollision( + config.maxVelocityToScale + ), + ) + .compose() + + // GIVEN a vibration at the upper bookend followed by a request to vibrate at progress + sliderHapticFeedbackProvider.onUpperBookend() + sliderHapticFeedbackProvider.onProgress(progress) + + // WHEN a vibration is to trigger again at the upper bookend + sliderHapticFeedbackProvider.onUpperBookend() + + // THEN there are two bookend vibrations + assertEquals( + /* expected= */ 2, + vibratorHelper.timesVibratedWithEffect(bookendVibration) ) - val ticks = VibrationEffect.startComposition() - repeat(config.numberOfLowTicks) { - ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) } - val bookendVibration = - VibrationEffect.startComposition() - .addPrimitive( - VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), - ) - .compose() - - // GIVEN a vibration at the upper bookend followed by a request to vibrate at progress - sliderHapticFeedbackProvider.onUpperBookend() - sliderHapticFeedbackProvider.onProgress(progress) - // WHEN a vibration is to trigger again at the upper bookend - sliderHapticFeedbackProvider.onUpperBookend() + fun dragTextureLastProgress_afterDragTextureHaptics_keepsLastDragTextureProgress() = + with(kosmos) { + // GIVEN max velocity and a slider progress at half progress + val progress = 0.5f - // THEN there are two bookend vibrations - verify(vibratorHelper, times(2)) - .vibrate(eq(bookendVibration), any(VibrationAttributes::class.java)) - } + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) - fun dragTextureLastProgress_afterDragTextureHaptics_keepsLastDragTextureProgress() { - // GIVEN max velocity and a slider progress at half progress - val progress = 0.5f + // WHEN a drag texture plays + sliderHapticFeedbackProvider.onProgress(progress) - // GIVEN system running for 1s - clock.advanceTime(1000) - - // WHEN a drag texture plays - sliderHapticFeedbackProvider.onProgress(progress) - - // THEN the dragTextureLastProgress remembers the latest progress - assertEquals(progress, sliderHapticFeedbackProvider.dragTextureLastProgress) - } + // THEN the dragTextureLastProgress remembers the latest progress + assertEquals(progress, sliderHapticFeedbackProvider.dragTextureLastProgress) + } @Test - fun dragTextureLastProgress_afterDragTextureHaptics_resetsOnHandleReleased() { - // GIVEN max velocity and a slider progress at half progress - val progress = 0.5f + fun dragTextureLastProgress_afterDragTextureHaptics_resetsOnHandleReleased() = + with(kosmos) { + // GIVEN max velocity and a slider progress at half progress + val progress = 0.5f - // GIVEN system running for 1s - clock.advanceTime(1000) + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) - // WHEN a drag texture plays - sliderHapticFeedbackProvider.onProgress(progress) + // WHEN a drag texture plays + sliderHapticFeedbackProvider.onProgress(progress) - // WHEN the handle is released - sliderHapticFeedbackProvider.onHandleReleasedFromTouch() + // WHEN the handle is released + sliderHapticFeedbackProvider.onHandleReleasedFromTouch() - // THEN the dragTextureLastProgress tracker is reset - assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress) - } + // THEN the dragTextureLastProgress tracker is reset + assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress) + } private fun generateTicksComposition(velocity: Float, progress: Float): VibrationEffect { val ticks = VibrationEffect.startComposition() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt index e0fff9c10873..04e214ac7a04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.tileimpl import android.content.Context import android.graphics.drawable.Drawable +import android.platform.test.annotations.EnableFlags import android.service.quicksettings.Tile import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -27,6 +28,7 @@ import android.view.View import android.view.accessibility.AccessibilityNodeInfo import android.widget.TextView import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.qs.QSTile @@ -380,6 +382,34 @@ class QSTileViewImplTest : SysuiTestCase() { assertThat(tileView.stateDescription?.contains(unavailableString)).isTrue() } + @Test + @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS) + fun onStateChange_longPressEffectActive_withInvalidDuration_doesNotCreateEffect() { + val state = QSTile.State() // A state that handles longPress + + // GIVEN an invalid long-press effect duration + tileView.constantLongPressEffectDuration = -1 + + // WHEN the state changes + tileView.changeState(state) + + // THEN the long-press effect is not created + assertThat(tileView.hasLongPressEffect).isFalse() + } + + @Test + @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS) + fun onStateChange_longPressEffectActive_withValidDuration_createsEffect() { + // GIVEN a test state that handles long-press and a valid long-press effect duration + val state = QSTile.State() + + // WHEN the state changes + tileView.changeState(state) + + // THEN the long-press effect created + assertThat(tileView.hasLongPressEffect).isTrue() + } + class FakeTileView( context: Context, collapsed: Boolean @@ -387,6 +417,9 @@ class QSTileViewImplTest : SysuiTestCase() { ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings), collapsed ) { + var constantLongPressEffectDuration = 500 + + override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration fun changeState(state: QSTile.State) { handleStateChanged(state) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt index 7c36a85243a2..7a83cfe852d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt @@ -19,7 +19,9 @@ package com.android.systemui.surfaceeffects.loadingeffect import android.graphics.Paint import android.graphics.RenderEffect import android.testing.AndroidTestingRunner +import android.testing.TestableLooper import androidx.test.filters.SmallTest +import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.model.SysUiStateTest import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState.EASE_IN @@ -31,18 +33,17 @@ import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.RenderEffectDrawCallback import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader -import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) class LoadingEffectTest : SysUiStateTest() { - private val fakeSystemClock = FakeSystemClock() - private val fakeExecutor = FakeExecutor(fakeSystemClock) + @get:Rule val animatorTestRule = AnimatorTestRule(this) @Test fun play_paintCallback_triggersDrawCallback() { @@ -61,14 +62,12 @@ class LoadingEffectTest : SysUiStateTest() { animationStateChangedCallback = null ) - fakeExecutor.execute { - assertThat(paintFromCallback).isNull() + assertThat(paintFromCallback).isNull() - loadingEffect.play() - fakeSystemClock.advanceTime(500L) + loadingEffect.play() + animatorTestRule.advanceTimeBy(500L) - assertThat(paintFromCallback).isNotNull() - } + assertThat(paintFromCallback).isNotNull() } @Test @@ -88,25 +87,22 @@ class LoadingEffectTest : SysUiStateTest() { animationStateChangedCallback = null ) - fakeExecutor.execute { - assertThat(renderEffectFromCallback).isNull() + assertThat(renderEffectFromCallback).isNull() - loadingEffect.play() - fakeSystemClock.advanceTime(500L) + loadingEffect.play() + animatorTestRule.advanceTimeBy(500L) - assertThat(renderEffectFromCallback).isNotNull() - } + assertThat(renderEffectFromCallback).isNotNull() } @Test fun play_animationStateChangesInOrder() { val config = TurbulenceNoiseAnimationConfig() - val expectedStates = arrayOf(NOT_PLAYING, EASE_IN, MAIN, EASE_OUT, NOT_PLAYING) - val actualStates = mutableListOf(NOT_PLAYING) + val states = mutableListOf(NOT_PLAYING) val stateChangedCallback = object : AnimationStateChangedCallback { override fun onStateChanged(oldState: AnimationState, newState: AnimationState) { - actualStates.add(newState) + states.add(newState) } } val drawCallback = @@ -121,16 +117,15 @@ class LoadingEffectTest : SysUiStateTest() { stateChangedCallback ) - val timeToAdvance = - config.easeInDuration + config.maxDuration + config.easeOutDuration + 100 + loadingEffect.play() - fakeExecutor.execute { - loadingEffect.play() + // Execute all the animators by advancing each duration with some buffer. + animatorTestRule.advanceTimeBy(config.easeInDuration.toLong()) + animatorTestRule.advanceTimeBy(config.maxDuration.toLong()) + animatorTestRule.advanceTimeBy(config.easeOutDuration.toLong()) + animatorTestRule.advanceTimeBy(500) - fakeSystemClock.advanceTime(timeToAdvance.toLong()) - - assertThat(actualStates).isEqualTo(expectedStates) - } + assertThat(states).containsExactly(NOT_PLAYING, EASE_IN, MAIN, EASE_OUT, NOT_PLAYING) } @Test @@ -157,17 +152,15 @@ class LoadingEffectTest : SysUiStateTest() { stateChangedCallback ) - fakeExecutor.execute { - assertThat(numPlay).isEqualTo(0) + assertThat(numPlay).isEqualTo(0) - loadingEffect.play() - loadingEffect.play() - loadingEffect.play() - loadingEffect.play() - loadingEffect.play() + loadingEffect.play() + loadingEffect.play() + loadingEffect.play() + loadingEffect.play() + loadingEffect.play() - assertThat(numPlay).isEqualTo(1) - } + assertThat(numPlay).isEqualTo(1) } @Test @@ -181,7 +174,7 @@ class LoadingEffectTest : SysUiStateTest() { val stateChangedCallback = object : AnimationStateChangedCallback { override fun onStateChanged(oldState: AnimationState, newState: AnimationState) { - if (oldState == MAIN && newState == NOT_PLAYING) { + if (oldState == EASE_OUT && newState == NOT_PLAYING) { isFinished = true } } @@ -194,18 +187,17 @@ class LoadingEffectTest : SysUiStateTest() { stateChangedCallback ) - fakeExecutor.execute { - assertThat(isFinished).isFalse() + assertThat(isFinished).isFalse() - loadingEffect.play() - fakeSystemClock.advanceTime(config.easeInDuration.toLong() + 500L) + loadingEffect.play() + animatorTestRule.advanceTimeBy(config.easeInDuration.toLong() + 500L) - assertThat(isFinished).isFalse() + assertThat(isFinished).isFalse() - loadingEffect.finish() + loadingEffect.finish() + animatorTestRule.advanceTimeBy(config.easeOutDuration.toLong() + 500L) - assertThat(isFinished).isTrue() - } + assertThat(isFinished).isTrue() } @Test @@ -232,13 +224,11 @@ class LoadingEffectTest : SysUiStateTest() { stateChangedCallback ) - fakeExecutor.execute { - assertThat(isFinished).isFalse() + assertThat(isFinished).isFalse() - loadingEffect.finish() + loadingEffect.finish() - assertThat(isFinished).isFalse() - } + assertThat(isFinished).isFalse() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt index 549280a809e2..e62ca645d772 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt @@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE_FRACTAL +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE_SPARKLE import org.junit.Test import org.junit.runner.RunWith @@ -38,4 +39,9 @@ class TurbulenceNoiseShaderTest : SysuiTestCase() { fun compilesFractalNoise() { turbulenceNoiseShader = TurbulenceNoiseShader(baseType = SIMPLEX_NOISE_FRACTAL) } + + @Test + fun compilesSparkleNoise() { + turbulenceNoiseShader = TurbulenceNoiseShader(baseType = SIMPLEX_NOISE_SPARKLE) + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/EmptyVibrator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/EmptyVibrator.kt new file mode 100644 index 000000000000..875f6ed8d4a8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/EmptyVibrator.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.haptics + +import android.os.VibrationAttributes +import android.os.VibrationEffect +import android.os.Vibrator + +/** A simple empty vibrator required for the [FakeVibratorHelper] */ +class EmptyVibrator : Vibrator() { + override fun cancel() {} + + override fun cancel(usageFilter: Int) {} + + override fun hasAmplitudeControl(): Boolean = true + + override fun hasVibrator(): Boolean = true + + override fun vibrate( + uid: Int, + opPkg: String, + vibe: VibrationEffect, + reason: String, + attributes: VibrationAttributes, + ) {} +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/FakeVibratorHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/FakeVibratorHelper.kt new file mode 100644 index 000000000000..4c0b132210f1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/FakeVibratorHelper.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.haptics + +import android.annotation.SuppressLint +import android.media.AudioAttributes +import android.os.VibrationAttributes +import android.os.VibrationEffect +import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock + +/** A fake [VibratorHelper] that only keeps track of the latest vibration effects delivered */ +@SuppressLint("VisibleForTests") +class FakeVibratorHelper : VibratorHelper(EmptyVibrator(), FakeExecutor(FakeSystemClock())) { + + /** A customizable map of primitive ids and their durations in ms */ + val primitiveDurations: HashMap<Int, Int> = ALL_PRIMITIVE_DURATIONS + + private val vibrationEffectHistory = ArrayList<VibrationEffect>() + + val totalVibrations: Int + get() = vibrationEffectHistory.size + + override fun vibrate(effect: VibrationEffect) { + vibrationEffectHistory.add(effect) + } + + override fun vibrate(effect: VibrationEffect, attributes: VibrationAttributes) = vibrate(effect) + + override fun vibrate(effect: VibrationEffect, attributes: AudioAttributes) = vibrate(effect) + + override fun vibrate( + uid: Int, + opPkg: String?, + vibe: VibrationEffect, + reason: String?, + attributes: VibrationAttributes, + ) = vibrate(vibe) + + override fun getPrimitiveDurations(vararg primitiveIds: Int): IntArray = + primitiveIds.map { primitiveDurations[it] ?: 0 }.toIntArray() + + fun hasVibratedWithEffects(vararg effects: VibrationEffect): Boolean = + vibrationEffectHistory.containsAll(effects.toList()) + + fun timesVibratedWithEffect(effect: VibrationEffect): Int = + vibrationEffectHistory.count { it == effect } + + companion object { + val ALL_PRIMITIVE_DURATIONS = + hashMapOf( + VibrationEffect.Composition.PRIMITIVE_NOOP to 0, + VibrationEffect.Composition.PRIMITIVE_CLICK to 12, + VibrationEffect.Composition.PRIMITIVE_THUD to 300, + VibrationEffect.Composition.PRIMITIVE_SPIN to 133, + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE to 150, + VibrationEffect.Composition.PRIMITIVE_SLOW_RISE to 500, + VibrationEffect.Composition.PRIMITIVE_QUICK_FALL to 100, + VibrationEffect.Composition.PRIMITIVE_TICK to 5, + VibrationEffect.Composition.PRIMITIVE_LOW_TICK to 12, + ) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt new file mode 100644 index 000000000000..434953fb2f43 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.haptics + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.vibratorHelper by Kosmos.Fixture { FakeVibratorHelper() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt index dad1887cbd85..f7de5a4c20c7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowKosmos.kt @@ -23,11 +23,13 @@ import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInterac import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.keyguardTransitionAnimationFlow by Fixture { KeyguardTransitionAnimationFlow( scope = applicationCoroutineScope, + mainDispatcher = testDispatcher, transitionInteractor = keyguardTransitionInteractor, logger = keyguardTransitionAnimationLogger, ) diff --git a/services/Android.bp b/services/Android.bp index 98a7979de30a..7bbb42e9a88f 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -253,6 +253,7 @@ java_library { required: [ "libukey2_jni_shared", + "protolog.conf.json.gz", ], lint: { baseline_filename: "lint-baseline.xml", diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 08093c0c037f..e64a87f3966b 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -45,7 +45,6 @@ import android.util.SparseArray; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.KnownPackages; import com.android.server.pm.PackageArchiver; import com.android.server.pm.PackageList; @@ -1396,21 +1395,6 @@ public abstract class PackageManagerInternal { @UserIdInt int userId, @Nullable String recentCallingPackage, @NonNull String debugInfo); - /** @deprecated For legacy shell command only. */ - @Deprecated - public abstract void legacyDumpProfiles(@NonNull String packageName, - boolean dumpClassesAndMethods) throws LegacyDexoptDisabledException; - - /** @deprecated For legacy shell command only. */ - @Deprecated - public abstract void legacyForceDexOpt(@NonNull String packageName) - throws LegacyDexoptDisabledException; - - /** @deprecated For legacy shell command only. */ - @Deprecated - public abstract void legacyReconcileSecondaryDexFiles(String packageName) - throws LegacyDexoptDisabledException; - /** * Gets {@link PackageManager.DistractionRestriction restrictions} of the given * packages of the given user. diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java index ecd14ce67d7e..cc4094092572 100644 --- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java +++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java @@ -77,23 +77,24 @@ public final class SensitiveContentProtectionManagerService extends SystemServic @Override public void onStart(MediaProjectionInfo info) { if (DEBUG) Log.d(TAG, "onStart projection: " + info); - Trace.beginSection( + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "SensitiveContentProtectionManagerService.onProjectionStart"); try { onProjectionStart(info.getPackageName()); } finally { - Trace.endSection(); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } } @Override public void onStop(MediaProjectionInfo info) { if (DEBUG) Log.d(TAG, "onStop projection: " + info); - Trace.beginSection("SensitiveContentProtectionManagerService.onProjectionStop"); + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, + "SensitiveContentProtectionManagerService.onProjectionStop"); try { onProjectionEnd(); } finally { - Trace.endSection(); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } } }; @@ -285,7 +286,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic @Override public void onListenerConnected() { super.onListenerConnected(); - Trace.beginSection("SensitiveContentProtectionManagerService.onListenerConnected"); + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, + "SensitiveContentProtectionManagerService.onListenerConnected"); try { // Projection started before notification listener was connected synchronized (mSensitiveContentProtectionLock) { @@ -294,14 +296,15 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } } } finally { - Trace.endSection(); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } } @Override public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { super.onNotificationPosted(sbn, rankingMap); - Trace.beginSection("SensitiveContentProtectionManagerService.onNotificationPosted"); + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, + "SensitiveContentProtectionManagerService.onNotificationPosted"); try { synchronized (mSensitiveContentProtectionLock) { if (!mProjectionActive) { @@ -317,14 +320,14 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } } } finally { - Trace.endSection(); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } } @Override public void onNotificationRankingUpdate(RankingMap rankingMap) { super.onNotificationRankingUpdate(rankingMap); - Trace.beginSection( + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "SensitiveContentProtectionManagerService.onNotificationRankingUpdate"); try { synchronized (mSensitiveContentProtectionLock) { @@ -333,7 +336,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } } } finally { - Trace.endSection(); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } } } @@ -382,7 +385,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic public void setSensitiveContentProtection(IBinder windowToken, String packageName, boolean isShowingSensitiveContent) { - Trace.beginSection( + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "SensitiveContentProtectionManagerService.setSensitiveContentProtection"); try { int callingUid = Binder.getCallingUid(); @@ -395,7 +398,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic Binder.restoreCallingIdentity(identity); } } finally { - Trace.endSection(); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 7df5fdd282c3..48d3c09290ce 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -127,6 +127,7 @@ public class SettingsToPropertiesMapper { "avic", "bluetooth", "brownout_mitigation_audio", + "brownout_mitigation_modem", "build", "biometrics", "biometrics_framework", @@ -168,6 +169,7 @@ public class SettingsToPropertiesMapper { "pixel_biometrics_face", "pixel_bluetooth", "pixel_connectivity_gps", + "pixel_sensors", "pixel_system_sw_video", "pixel_watch", "platform_compat", diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java index d93ff9dac91f..086f3aa8ad65 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java @@ -138,7 +138,9 @@ public final class BroadcastRadioServiceImpl { /** * Constructs BroadcastRadioServiceImpl using AIDL HAL using the list of names of AIDL - * BroadcastRadio HAL services {@code serviceNameList} + * BroadcastRadio HAL services + * + * @param serviceNameList list of names of AIDL BroadcastRadio HAL services */ public BroadcastRadioServiceImpl(ArrayList<String> serviceNameList) { mNextModuleId = 0; @@ -169,7 +171,11 @@ public final class BroadcastRadioServiceImpl { } /** - * Gets the AIDL RadioModule for the given {@code moduleId}. Null will be returned if not found. + * Gets the AIDL RadioModule for the given module Id. + * + * @param id Id of {@link RadioModule} of AIDL BroadcastRadio HAL service + * @return {@code true} if {@link RadioModule} of AIDL BroadcastRadio HAL service is found, + * {@code false} otherwise */ public boolean hasModule(int id) { synchronized (mLock) { diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java index 18ba2cf1405e..9ba88aa18ce6 100644 --- a/services/core/java/com/android/server/pm/AppDataHelper.java +++ b/services/core/java/com/android/server/pm/AppDataHelper.java @@ -45,7 +45,6 @@ import android.util.TimingsTraceLog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.SystemServerInitThreadPool; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.AndroidPackage; @@ -256,41 +255,6 @@ public class AppDataHelper { } } - if (!DexOptHelper.useArtService()) { // ART Service handles this on demand instead. - // Prepare the application profiles only for upgrades and - // first boot (so that we don't repeat the same operation at - // each boot). - // - // We only have to cover the upgrade and first boot here - // because for app installs we prepare the profiles before - // invoking dexopt (in installPackageLI). - // - // We also have to cover non system users because we do not - // call the usual install package methods for them. - // - // NOTE: in order to speed up first boot time we only create - // the current profile and do not update the content of the - // reference profile. A system image should already be - // configured with the right profile keys and the profiles - // for the speed-profile prebuilds should already be copied. - // That's done in #performDexOptUpgrade. - // - // TODO(calin, mathieuc): We should use .dm files for - // prebuilds profiles instead of manually copying them in - // #performDexOptUpgrade. When we do that we should have a - // more granular check here and only update the existing - // profiles. - if (pkg != null && (mPm.isDeviceUpgrading() || mPm.isFirstBoot() - || (userId != UserHandle.USER_SYSTEM))) { - try { - mArtManagerService.prepareAppProfiles(pkg, userId, - /* updateReferenceProfileContent= */ false); - } catch (LegacyDexoptDisabledException e2) { - throw new RuntimeException(e2); - } - } - } - final long ceDataInode = createAppDataResult.ceDataInode; final long deDataInode = createAppDataResult.deDataInode; @@ -615,15 +579,7 @@ public class AppDataHelper { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } - if (DexOptHelper.useArtService()) { - destroyAppProfilesWithArtService(pkg.getPackageName()); - } else { - try { - mArtManagerService.clearAppProfiles(pkg); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } + destroyAppProfilesLIF(pkg.getPackageName()); } public void destroyAppDataLIF(AndroidPackage pkg, int userId, int flags) { @@ -657,20 +613,6 @@ public class AppDataHelper { * Destroy ART app profiles for the package. */ void destroyAppProfilesLIF(String packageName) { - if (DexOptHelper.useArtService()) { - destroyAppProfilesWithArtService(packageName); - } else { - try { - mInstaller.destroyAppProfiles(packageName); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } catch (Installer.InstallerException e) { - Slog.w(TAG, String.valueOf(e)); - } - } - } - - private void destroyAppProfilesWithArtService(String packageName) { if (!DexOptHelper.artManagerLocalIsInitialized()) { // This function may get called while PackageManagerService is constructed (via e.g. // InitAppsHelper.initSystemApps), and ART Service hasn't yet been started then (it diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptJobService.java b/services/core/java/com/android/server/pm/BackgroundDexOptJobService.java deleted file mode 100644 index d9452742f99c..000000000000 --- a/services/core/java/com/android/server/pm/BackgroundDexOptJobService.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2021 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.pm; - -import android.app.job.JobParameters; -import android.app.job.JobService; - -/** - * JobService to run background dex optimization. This is a thin wrapper and most logic exits in - * {@link BackgroundDexOptService}. - */ -public final class BackgroundDexOptJobService extends JobService { - - @Override - public boolean onStartJob(JobParameters params) { - return BackgroundDexOptService.getService().onStartJob(this, params); - } - - @Override - public boolean onStopJob(JobParameters params) { - return BackgroundDexOptService.getService().onStopJob(this, params); - } -} diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java deleted file mode 100644 index 36677df07ca3..000000000000 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ /dev/null @@ -1,1152 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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.pm; - -import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; -import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; -import static com.android.server.pm.dex.ArtStatsLogUtils.BackgroundDexoptJobStatsLogger; - -import static dalvik.system.DexFile.isProfileGuidedCompilerFilter; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.job.JobInfo; -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageInfo; -import android.os.BatteryManagerInternal; -import android.os.Binder; -import android.os.Environment; -import android.os.IThermalService; -import android.os.PowerManager; -import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.Trace; -import android.os.UserHandle; -import android.os.storage.StorageManager; -import android.util.ArraySet; -import android.util.Log; -import android.util.Slog; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.FunctionalUtils.ThrowingCheckedSupplier; -import com.android.internal.util.IndentingPrintWriter; -import com.android.server.LocalServices; -import com.android.server.PinnerService; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; -import com.android.server.pm.PackageDexOptimizer.DexOptResult; -import com.android.server.pm.dex.DexManager; -import com.android.server.pm.dex.DexoptOptions; -import com.android.server.utils.TimingsTraceAndSlog; - -import java.io.File; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * Controls background dex optimization run as idle job or command line. - */ -public final class BackgroundDexOptService { - private static final String TAG = "BackgroundDexOptService"; - - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - @VisibleForTesting static final int JOB_IDLE_OPTIMIZE = 800; - @VisibleForTesting static final int JOB_POST_BOOT_UPDATE = 801; - - private static final long IDLE_OPTIMIZATION_PERIOD = TimeUnit.DAYS.toMillis(1); - - private static final long CANCELLATION_WAIT_CHECK_INTERVAL_MS = 200; - - private static final ComponentName sDexoptServiceName = - new ComponentName("android", BackgroundDexOptJobService.class.getName()); - - // Possible return codes of individual optimization steps. - /** Initial value. */ - public static final int STATUS_UNSPECIFIED = -1; - /** Ok status: Optimizations finished, All packages were processed, can continue */ - public static final int STATUS_OK = 0; - /** Optimizations should be aborted. Job scheduler requested it. */ - public static final int STATUS_ABORT_BY_CANCELLATION = 1; - /** Optimizations should be aborted. No space left on device. */ - public static final int STATUS_ABORT_NO_SPACE_LEFT = 2; - /** Optimizations should be aborted. Thermal throttling level too high. */ - public static final int STATUS_ABORT_THERMAL = 3; - /** Battery level too low */ - public static final int STATUS_ABORT_BATTERY = 4; - /** - * {@link PackageDexOptimizer#DEX_OPT_FAILED} case. This state means some packages have failed - * compilation during the job. Note that the failure will not be permanent as the next dexopt - * job will exclude those failed packages. - */ - public static final int STATUS_DEX_OPT_FAILED = 5; - /** Encountered fatal error, such as a runtime exception. */ - public static final int STATUS_FATAL_ERROR = 6; - - @IntDef(prefix = {"STATUS_"}, - value = - { - STATUS_UNSPECIFIED, - STATUS_OK, - STATUS_ABORT_BY_CANCELLATION, - STATUS_ABORT_NO_SPACE_LEFT, - STATUS_ABORT_THERMAL, - STATUS_ABORT_BATTERY, - STATUS_DEX_OPT_FAILED, - STATUS_FATAL_ERROR, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Status {} - - // Used for calculating space threshold for downgrading unused apps. - private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2; - - // Thermal cutoff value used if one isn't defined by a system property. - private static final int THERMAL_CUTOFF_DEFAULT = PowerManager.THERMAL_STATUS_MODERATE; - - private final Injector mInjector; - - private final DexOptHelper mDexOptHelper; - - private final BackgroundDexoptJobStatsLogger mStatsLogger = - new BackgroundDexoptJobStatsLogger(); - - private final Object mLock = new Object(); - - // Thread currently running dexopt. This will be null if dexopt is not running. - // The thread running dexopt make sure to set this into null when the pending dexopt is - // completed. - @GuardedBy("mLock") @Nullable private Thread mDexOptThread; - - // Thread currently cancelling dexopt. This thread is in blocked wait state until - // cancellation is done. Only this thread can change states for control. The other threads, if - // need to wait for cancellation, should just wait without doing any control. - @GuardedBy("mLock") @Nullable private Thread mDexOptCancellingThread; - - // Tells whether post boot update is completed or not. - @GuardedBy("mLock") private boolean mFinishedPostBootUpdate; - - // True if JobScheduler invocations of dexopt have been disabled. - @GuardedBy("mLock") private boolean mDisableJobSchedulerJobs; - - @GuardedBy("mLock") @Status private int mLastExecutionStatus = STATUS_UNSPECIFIED; - - @GuardedBy("mLock") private long mLastExecutionStartUptimeMs; - @GuardedBy("mLock") private long mLastExecutionDurationMs; - - // Keeps packages cancelled from PDO for last session. This is for debugging. - @GuardedBy("mLock") - private final ArraySet<String> mLastCancelledPackages = new ArraySet<String>(); - - /** - * Set of failed packages remembered across job runs. - */ - @GuardedBy("mLock") - private final ArraySet<String> mFailedPackageNamesPrimary = new ArraySet<String>(); - @GuardedBy("mLock") - private final ArraySet<String> mFailedPackageNamesSecondary = new ArraySet<String>(); - - private final long mDowngradeUnusedAppsThresholdInMillis; - - private final List<PackagesUpdatedListener> mPackagesUpdatedListeners = new ArrayList<>(); - - private int mThermalStatusCutoff = THERMAL_CUTOFF_DEFAULT; - - /** Listener for monitoring package change due to dexopt. */ - public interface PackagesUpdatedListener { - /** Called when the packages are updated through dexopt */ - void onPackagesUpdated(ArraySet<String> updatedPackages); - } - - public BackgroundDexOptService(Context context, DexManager dexManager, PackageManagerService pm) - throws LegacyDexoptDisabledException { - this(new Injector(context, dexManager, pm)); - } - - @VisibleForTesting - public BackgroundDexOptService(Injector injector) throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - mInjector = injector; - mDexOptHelper = mInjector.getDexOptHelper(); - LocalServices.addService(BackgroundDexOptService.class, this); - mDowngradeUnusedAppsThresholdInMillis = mInjector.getDowngradeUnusedAppsThresholdInMillis(); - } - - /** Start scheduling job after boot completion */ - public void systemReady() throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - if (mInjector.isBackgroundDexOptDisabled()) { - return; - } - - mInjector.getContext().registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - mInjector.getContext().unregisterReceiver(this); - // queue both job. JOB_IDLE_OPTIMIZE will not start until JOB_POST_BOOT_UPDATE is - // completed. - scheduleAJob(JOB_POST_BOOT_UPDATE); - scheduleAJob(JOB_IDLE_OPTIMIZE); - if (DEBUG) { - Slog.d(TAG, "BootBgDexopt scheduled"); - } - } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); - } - - /** Dump the current state */ - public void dump(IndentingPrintWriter writer) { - boolean disabled = mInjector.isBackgroundDexOptDisabled(); - writer.print("enabled:"); - writer.println(!disabled); - if (disabled) { - return; - } - synchronized (mLock) { - writer.print("mDexOptThread:"); - writer.println(mDexOptThread); - writer.print("mDexOptCancellingThread:"); - writer.println(mDexOptCancellingThread); - writer.print("mFinishedPostBootUpdate:"); - writer.println(mFinishedPostBootUpdate); - writer.print("mDisableJobSchedulerJobs:"); - writer.println(mDisableJobSchedulerJobs); - writer.print("mLastExecutionStatus:"); - writer.println(mLastExecutionStatus); - writer.print("mLastExecutionStartUptimeMs:"); - writer.println(mLastExecutionStartUptimeMs); - writer.print("mLastExecutionDurationMs:"); - writer.println(mLastExecutionDurationMs); - writer.print("now:"); - writer.println(SystemClock.elapsedRealtime()); - writer.print("mLastCancelledPackages:"); - writer.println(String.join(",", mLastCancelledPackages)); - writer.print("mFailedPackageNamesPrimary:"); - writer.println(String.join(",", mFailedPackageNamesPrimary)); - writer.print("mFailedPackageNamesSecondary:"); - writer.println(String.join(",", mFailedPackageNamesSecondary)); - } - } - - /** Gets the instance of the service */ - public static BackgroundDexOptService getService() { - return LocalServices.getService(BackgroundDexOptService.class); - } - - /** - * Executes the background dexopt job immediately for selected packages or all packages. - * - * <p>This is only for shell command and only root or shell user can use this. - * - * @param packageNames dex optimize the passed packages in the given order, or all packages in - * the default order if null - * - * @return true if dex optimization is complete. false if the task is cancelled or if there was - * an error. - */ - public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) - throws LegacyDexoptDisabledException { - enforceRootOrShell(); - long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - // Do not cancel and wait for completion if there is pending task. - waitForDexOptThreadToFinishLocked(); - resetStatesForNewDexOptRunLocked(Thread.currentThread()); - } - PackageManagerService pm = mInjector.getPackageManagerService(); - List<String> packagesToOptimize; - if (packageNames == null) { - packagesToOptimize = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer()); - } else { - packagesToOptimize = packageNames; - } - return runIdleOptimization(pm, packagesToOptimize, /* isPostBootUpdate= */ false); - } finally { - Binder.restoreCallingIdentity(identity); - markDexOptCompleted(); - } - } - - /** - * Cancels currently running any idle optimization tasks started from JobScheduler - * or runIdleOptimization call. - * - * <p>This is only for shell command and only root or shell user can use this. - */ - public void cancelBackgroundDexoptJob() throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - enforceRootOrShell(); - Binder.withCleanCallingIdentity(() -> cancelDexOptAndWaitForCompletion()); - } - - /** - * Sets a flag that disables jobs from being started from JobScheduler. - * - * This state is not persistent and is only retained in this service instance. - * - * This is intended for shell command use and only root or shell users can call it. - * - * @param disable True if JobScheduler invocations should be disabled, false otherwise. - */ - public void setDisableJobSchedulerJobs(boolean disable) throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - enforceRootOrShell(); - synchronized (mLock) { - mDisableJobSchedulerJobs = disable; - } - } - - /** Adds listener for package update */ - public void addPackagesUpdatedListener(PackagesUpdatedListener listener) - throws LegacyDexoptDisabledException { - // TODO(b/251903639): Evaluate whether this needs to support ART Service or not. - Installer.checkLegacyDexoptDisabled(); - synchronized (mLock) { - mPackagesUpdatedListeners.add(listener); - } - } - - /** Removes package update listener */ - public void removePackagesUpdatedListener(PackagesUpdatedListener listener) - throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - synchronized (mLock) { - mPackagesUpdatedListeners.remove(listener); - } - } - - /** - * Notifies package change and removes the package from the failed package list so that - * the package can run dexopt again. - */ - public void notifyPackageChanged(String packageName) throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - // The idle maintenance job skips packages which previously failed to - // compile. The given package has changed and may successfully compile - // now. Remove it from the list of known failing packages. - synchronized (mLock) { - mFailedPackageNamesPrimary.remove(packageName); - mFailedPackageNamesSecondary.remove(packageName); - } - } - - /** For BackgroundDexOptJobService to dispatch onStartJob event */ - /* package */ boolean onStartJob(BackgroundDexOptJobService job, JobParameters params) { - Slog.i(TAG, "onStartJob:" + params.getJobId()); - - boolean isPostBootUpdateJob = params.getJobId() == JOB_POST_BOOT_UPDATE; - // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from - // the checks above. This check is not "live" - the value is determined by a background - // restart with a period of ~1 minute. - PackageManagerService pm = mInjector.getPackageManagerService(); - if (pm.isStorageLow()) { - Slog.w(TAG, "Low storage, skipping this run"); - markPostBootUpdateCompleted(params); - return false; - } - - List<String> pkgs = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer()); - if (pkgs.isEmpty()) { - Slog.i(TAG, "No packages to optimize"); - markPostBootUpdateCompleted(params); - return false; - } - - mThermalStatusCutoff = mInjector.getDexOptThermalCutoff(); - - synchronized (mLock) { - if (mDisableJobSchedulerJobs) { - Slog.i(TAG, "JobScheduler invocations disabled"); - return false; - } - if (mDexOptThread != null && mDexOptThread.isAlive()) { - // Other task is already running. - return false; - } - if (!isPostBootUpdateJob && !mFinishedPostBootUpdate) { - // Post boot job not finished yet. Run post boot job first. - return false; - } - try { - resetStatesForNewDexOptRunLocked(mInjector.createAndStartThread( - "BackgroundDexOptService_" + (isPostBootUpdateJob ? "PostBoot" : "Idle"), - () -> { - TimingsTraceAndSlog tr = - new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_DALVIK); - tr.traceBegin("jobExecution"); - boolean completed = false; - boolean fatalError = false; - try { - completed = runIdleOptimization( - pm, pkgs, params.getJobId() == JOB_POST_BOOT_UPDATE); - } catch (LegacyDexoptDisabledException e) { - Slog.wtf(TAG, e); - } catch (RuntimeException e) { - fatalError = true; - throw e; - } finally { // Those cleanup should be done always. - tr.traceEnd(); - Slog.i(TAG, - "dexopt finishing. jobid:" + params.getJobId() - + " completed:" + completed); - - writeStatsLog(params); - - if (params.getJobId() == JOB_POST_BOOT_UPDATE) { - if (completed) { - markPostBootUpdateCompleted(params); - } - } - // Reschedule when cancelled. No need to reschedule when failed with - // fatal error because it's likely to fail again. - job.jobFinished(params, !completed && !fatalError); - markDexOptCompleted(); - } - })); - } catch (LegacyDexoptDisabledException e) { - Slog.wtf(TAG, e); - } - } - return true; - } - - /** For BackgroundDexOptJobService to dispatch onStopJob event */ - /* package */ boolean onStopJob(BackgroundDexOptJobService job, JobParameters params) { - Slog.i(TAG, "onStopJob:" + params.getJobId()); - // This cannot block as it is in main thread, thus dispatch to a newly created thread - // and cancel it from there. As this event does not happen often, creating a new thread - // is justified rather than having one thread kept permanently. - mInjector.createAndStartThread("DexOptCancel", () -> { - try { - cancelDexOptAndWaitForCompletion(); - } catch (LegacyDexoptDisabledException e) { - Slog.wtf(TAG, e); - } - }); - // Always reschedule for cancellation. - return true; - } - - /** - * Cancels pending dexopt and wait for completion of the cancellation. This can block the caller - * until cancellation is done. - */ - private void cancelDexOptAndWaitForCompletion() throws LegacyDexoptDisabledException { - synchronized (mLock) { - if (mDexOptThread == null) { - return; - } - if (mDexOptCancellingThread != null && mDexOptCancellingThread.isAlive()) { - // No control, just wait - waitForDexOptThreadToFinishLocked(); - // Do not wait for other cancellation's complete. That will be handled by the next - // start flow. - return; - } - mDexOptCancellingThread = Thread.currentThread(); - // Take additional caution to make sure that we do not leave this call - // with controlDexOptBlockingLocked(true) state. - try { - controlDexOptBlockingLocked(true); - waitForDexOptThreadToFinishLocked(); - } finally { - // Reset to default states regardless of previous states - mDexOptCancellingThread = null; - mDexOptThread = null; - controlDexOptBlockingLocked(false); - mLock.notifyAll(); - } - } - } - - @GuardedBy("mLock") - private void waitForDexOptThreadToFinishLocked() { - TimingsTraceAndSlog tr = new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER); - // This tracing section doesn't have any correspondence in ART Service - it never waits for - // cancellation to finish. - tr.traceBegin("waitForDexOptThreadToFinishLocked"); - try { - // Wait but check in regular internal to see if the thread is still alive. - while (mDexOptThread != null && mDexOptThread.isAlive()) { - mLock.wait(CANCELLATION_WAIT_CHECK_INTERVAL_MS); - } - } catch (InterruptedException e) { - Slog.w(TAG, "Interrupted while waiting for dexopt thread"); - Thread.currentThread().interrupt(); - } - tr.traceEnd(); - } - - private void markDexOptCompleted() { - synchronized (mLock) { - if (mDexOptThread != Thread.currentThread()) { - throw new IllegalStateException( - "Only mDexOptThread can mark completion, mDexOptThread:" + mDexOptThread - + " current:" + Thread.currentThread()); - } - mDexOptThread = null; - // Other threads may be waiting for completion. - mLock.notifyAll(); - } - } - - @GuardedBy("mLock") - private void resetStatesForNewDexOptRunLocked(Thread thread) - throws LegacyDexoptDisabledException { - mDexOptThread = thread; - mLastCancelledPackages.clear(); - controlDexOptBlockingLocked(false); - } - - private void enforceRootOrShell() { - int uid = mInjector.getCallingUid(); - if (uid != Process.ROOT_UID && uid != Process.SHELL_UID) { - throw new SecurityException("Should be shell or root user"); - } - } - - @GuardedBy("mLock") - private void controlDexOptBlockingLocked(boolean block) throws LegacyDexoptDisabledException { - PackageManagerService pm = mInjector.getPackageManagerService(); - mDexOptHelper.controlDexOptBlocking(block); - } - - private void scheduleAJob(int jobId) { - JobScheduler js = mInjector.getJobScheduler(); - JobInfo.Builder builder = - new JobInfo.Builder(jobId, sDexoptServiceName).setRequiresDeviceIdle(true); - if (jobId == JOB_IDLE_OPTIMIZE) { - builder.setRequiresCharging(true).setPeriodic(IDLE_OPTIMIZATION_PERIOD); - } - js.schedule(builder.build()); - } - - private long getLowStorageThreshold() { - long lowThreshold = mInjector.getDataDirStorageLowBytes(); - if (lowThreshold == 0) { - Slog.e(TAG, "Invalid low storage threshold"); - } - - return lowThreshold; - } - - private void logStatus(int status) { - switch (status) { - case STATUS_OK: - Slog.i(TAG, "Idle optimizations completed."); - break; - case STATUS_ABORT_NO_SPACE_LEFT: - Slog.w(TAG, "Idle optimizations aborted because of space constraints."); - break; - case STATUS_ABORT_BY_CANCELLATION: - Slog.w(TAG, "Idle optimizations aborted by cancellation."); - break; - case STATUS_ABORT_THERMAL: - Slog.w(TAG, "Idle optimizations aborted by thermal throttling."); - break; - case STATUS_ABORT_BATTERY: - Slog.w(TAG, "Idle optimizations aborted by low battery."); - break; - case STATUS_DEX_OPT_FAILED: - Slog.w(TAG, "Idle optimizations failed from dexopt."); - break; - default: - Slog.w(TAG, "Idle optimizations ended with unexpected code: " + status); - break; - } - } - - /** - * Returns whether we've successfully run the job. Note that it will return true even if some - * packages may have failed compiling. - */ - private boolean runIdleOptimization(PackageManagerService pm, List<String> pkgs, - boolean isPostBootUpdate) throws LegacyDexoptDisabledException { - synchronized (mLock) { - mLastExecutionStatus = STATUS_UNSPECIFIED; - mLastExecutionStartUptimeMs = SystemClock.uptimeMillis(); - mLastExecutionDurationMs = -1; - } - - int status = STATUS_UNSPECIFIED; - try { - long lowStorageThreshold = getLowStorageThreshold(); - status = idleOptimizePackages(pm, pkgs, lowStorageThreshold, isPostBootUpdate); - logStatus(status); - return status == STATUS_OK || status == STATUS_DEX_OPT_FAILED; - } catch (RuntimeException e) { - status = STATUS_FATAL_ERROR; - throw e; - } finally { - synchronized (mLock) { - mLastExecutionStatus = status; - mLastExecutionDurationMs = SystemClock.uptimeMillis() - mLastExecutionStartUptimeMs; - } - } - } - - /** Gets the size of the directory. It uses recursion to go over all files. */ - private long getDirectorySize(File f) { - long size = 0; - if (f.isDirectory()) { - for (File file : f.listFiles()) { - size += getDirectorySize(file); - } - } else { - size = f.length(); - } - return size; - } - - /** Gets the size of a package. */ - private long getPackageSize(@NonNull Computer snapshot, String pkg) { - // TODO(b/251903639): Make this in line with the calculation in - // `DexOptHelper.DexoptDoneHandler`. - PackageInfo info = snapshot.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); - long size = 0; - if (info != null && info.applicationInfo != null) { - File path = Paths.get(info.applicationInfo.sourceDir).toFile(); - if (path.isFile()) { - path = path.getParentFile(); - } - size += getDirectorySize(path); - if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) { - for (String splitSourceDir : info.applicationInfo.splitSourceDirs) { - File pathSplitSourceDir = Paths.get(splitSourceDir).toFile(); - if (pathSplitSourceDir.isFile()) { - pathSplitSourceDir = pathSplitSourceDir.getParentFile(); - } - if (path.getAbsolutePath().equals(pathSplitSourceDir.getAbsolutePath())) { - continue; - } - size += getDirectorySize(pathSplitSourceDir); - } - } - return size; - } - return 0; - } - - @Status - private int idleOptimizePackages(PackageManagerService pm, List<String> pkgs, - long lowStorageThreshold, boolean isPostBootUpdate) - throws LegacyDexoptDisabledException { - ArraySet<String> updatedPackages = new ArraySet<>(); - - try { - boolean supportSecondaryDex = mInjector.supportSecondaryDex(); - - if (supportSecondaryDex) { - @Status int result = reconcileSecondaryDexFiles(); - if (result != STATUS_OK) { - return result; - } - } - - // Only downgrade apps when space is low on device. - // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean - // up disk before user hits the actual lowStorageThreshold. - long lowStorageThresholdForDowngrade = - LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE * lowStorageThreshold; - boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); - if (DEBUG) { - Slog.d(TAG, "Should Downgrade " + shouldDowngrade); - } - if (shouldDowngrade) { - final Computer snapshot = pm.snapshotComputer(); - Set<String> unusedPackages = - snapshot.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); - if (DEBUG) { - Slog.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); - } - - if (!unusedPackages.isEmpty()) { - for (String pkg : unusedPackages) { - @Status int abortCode = abortIdleOptimizations(/*lowStorageThreshold*/ -1); - if (abortCode != STATUS_OK) { - // Should be aborted by the scheduler. - return abortCode; - } - @DexOptResult - int downgradeResult = downgradePackage(snapshot, pm, pkg, - /* isForPrimaryDex= */ true, isPostBootUpdate); - if (downgradeResult == PackageDexOptimizer.DEX_OPT_PERFORMED) { - updatedPackages.add(pkg); - } - @Status - int status = convertPackageDexOptimizerStatusToInternal(downgradeResult); - if (status != STATUS_OK) { - return status; - } - if (supportSecondaryDex) { - downgradeResult = downgradePackage(snapshot, pm, pkg, - /* isForPrimaryDex= */ false, isPostBootUpdate); - status = convertPackageDexOptimizerStatusToInternal(downgradeResult); - if (status != STATUS_OK) { - return status; - } - } - } - - pkgs = new ArrayList<>(pkgs); - pkgs.removeAll(unusedPackages); - } - } - - return optimizePackages(pkgs, lowStorageThreshold, updatedPackages, isPostBootUpdate); - } finally { - // Always let the pinner service know about changes. - // TODO(b/251903639): ART Service does this for all dexopts, while the code below only - // runs for background jobs. We should try to make them behave the same. - notifyPinService(updatedPackages); - // Only notify IORap the primary dex opt, because we don't want to - // invalidate traces unnecessary due to b/161633001 and that it's - // better to have a trace than no trace at all. - notifyPackagesUpdated(updatedPackages); - } - } - - @Status - private int optimizePackages(List<String> pkgs, long lowStorageThreshold, - ArraySet<String> updatedPackages, boolean isPostBootUpdate) - throws LegacyDexoptDisabledException { - boolean supportSecondaryDex = mInjector.supportSecondaryDex(); - - // Keep the error if there is any error from any package. - @Status int status = STATUS_OK; - - // Other than cancellation, all packages will be processed even if an error happens - // in a package. - for (String pkg : pkgs) { - int abortCode = abortIdleOptimizations(lowStorageThreshold); - if (abortCode != STATUS_OK) { - // Either aborted by the scheduler or no space left. - return abortCode; - } - - @DexOptResult - int primaryResult = optimizePackage(pkg, true /* isForPrimaryDex */, isPostBootUpdate); - if (primaryResult == PackageDexOptimizer.DEX_OPT_CANCELLED) { - return STATUS_ABORT_BY_CANCELLATION; - } - if (primaryResult == PackageDexOptimizer.DEX_OPT_PERFORMED) { - updatedPackages.add(pkg); - } else if (primaryResult == PackageDexOptimizer.DEX_OPT_FAILED) { - status = convertPackageDexOptimizerStatusToInternal(primaryResult); - } - - if (!supportSecondaryDex) { - continue; - } - - @DexOptResult - int secondaryResult = - optimizePackage(pkg, false /* isForPrimaryDex */, isPostBootUpdate); - if (secondaryResult == PackageDexOptimizer.DEX_OPT_CANCELLED) { - return STATUS_ABORT_BY_CANCELLATION; - } - if (secondaryResult == PackageDexOptimizer.DEX_OPT_FAILED) { - status = convertPackageDexOptimizerStatusToInternal(secondaryResult); - } - } - return status; - } - - /** - * Try to downgrade the package to a smaller compilation filter. - * eg. if the package is in speed-profile the package will be downgraded to verify. - * @param pm PackageManagerService - * @param pkg The package to be downgraded. - * @param isForPrimaryDex Apps can have several dex file, primary and secondary. - * @return PackageDexOptimizer.DEX_* - */ - @DexOptResult - private int downgradePackage(@NonNull Computer snapshot, PackageManagerService pm, String pkg, - boolean isForPrimaryDex, boolean isPostBootUpdate) - throws LegacyDexoptDisabledException { - if (DEBUG) { - Slog.d(TAG, "Downgrading " + pkg); - } - if (isCancelling()) { - return PackageDexOptimizer.DEX_OPT_CANCELLED; - } - int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE; - String filter = getCompilerFilterForReason(reason); - int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_DOWNGRADE; - - if (isProfileGuidedCompilerFilter(filter)) { - // We don't expect updates in current profiles to be significant here, but - // DEXOPT_CHECK_FOR_PROFILES_UPDATES is set to replicate behaviour that will be - // unconditionally enabled for profile guided filters when ART Service is called instead - // of the legacy PackageDexOptimizer implementation. - dexoptFlags |= DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES; - } - - if (!isPostBootUpdate) { - dexoptFlags |= DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB; - } - - long package_size_before = getPackageSize(snapshot, pkg); - int result = PackageDexOptimizer.DEX_OPT_SKIPPED; - if (isForPrimaryDex || PLATFORM_PACKAGE_NAME.equals(pkg)) { - // This applies for system apps or if packages location is not a directory, i.e. - // monolithic install. - if (!pm.canHaveOatDir(snapshot, pkg)) { - // For apps that don't have the oat directory, instead of downgrading, - // remove their compiler artifacts from dalvik cache. - pm.deleteOatArtifactsOfPackage(snapshot, pkg); - } else { - result = performDexOptPrimary(pkg, reason, filter, dexoptFlags); - } - } else { - result = performDexOptSecondary(pkg, reason, filter, dexoptFlags); - } - - if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) { - final Computer newSnapshot = pm.snapshotComputer(); - FrameworkStatsLog.write(FrameworkStatsLog.APP_DOWNGRADED, pkg, package_size_before, - getPackageSize(newSnapshot, pkg), /*aggressive=*/false); - } - return result; - } - - @Status - private int reconcileSecondaryDexFiles() throws LegacyDexoptDisabledException { - // TODO(calin): should we denylist packages for which we fail to reconcile? - for (String p : mInjector.getDexManager().getAllPackagesWithSecondaryDexFiles()) { - if (isCancelling()) { - return STATUS_ABORT_BY_CANCELLATION; - } - mInjector.getDexManager().reconcileSecondaryDexFiles(p); - } - return STATUS_OK; - } - - /** - * - * Optimize package if needed. Note that there can be no race between - * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized. - * @param pkg The package to be downgraded. - * @param isForPrimaryDex Apps can have several dex file, primary and secondary. - * @param isPostBootUpdate is post boot update or not. - * @return PackageDexOptimizer#DEX_OPT_* - */ - @DexOptResult - private int optimizePackage(String pkg, boolean isForPrimaryDex, boolean isPostBootUpdate) - throws LegacyDexoptDisabledException { - int reason = isPostBootUpdate ? PackageManagerService.REASON_POST_BOOT - : PackageManagerService.REASON_BACKGROUND_DEXOPT; - String filter = getCompilerFilterForReason(reason); - - int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE; - if (!isPostBootUpdate) { - dexoptFlags |= DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES - | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB; - } - - if (isProfileGuidedCompilerFilter(filter)) { - // Ensure DEXOPT_CHECK_FOR_PROFILES_UPDATES is enabled if the filter is profile guided, - // to replicate behaviour that will be unconditionally enabled when ART Service is - // called instead of the legacy PackageDexOptimizer implementation. - dexoptFlags |= DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES; - } - - // System server share the same code path as primary dex files. - // PackageManagerService will select the right optimization path for it. - if (isForPrimaryDex || PLATFORM_PACKAGE_NAME.equals(pkg)) { - return performDexOptPrimary(pkg, reason, filter, dexoptFlags); - } else { - return performDexOptSecondary(pkg, reason, filter, dexoptFlags); - } - } - - @DexOptResult - private int performDexOptPrimary(String pkg, int reason, String filter, int dexoptFlags) - throws LegacyDexoptDisabledException { - DexoptOptions dexoptOptions = - new DexoptOptions(pkg, reason, filter, /*splitName=*/null, dexoptFlags); - return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/true, - () -> mDexOptHelper.performDexOptWithStatus(dexoptOptions)); - } - - @DexOptResult - private int performDexOptSecondary(String pkg, int reason, String filter, int dexoptFlags) - throws LegacyDexoptDisabledException { - DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason, filter, /*splitName=*/null, - dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX); - return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/false, - () - -> mDexOptHelper.performDexOpt(dexoptOptions) - ? PackageDexOptimizer.DEX_OPT_PERFORMED - : PackageDexOptimizer.DEX_OPT_FAILED); - } - - /** - * Execute the dexopt wrapper and make sure that if performDexOpt wrapper fails - * the package is added to the list of failed packages. - * Return one of following result: - * {@link PackageDexOptimizer#DEX_OPT_SKIPPED} - * {@link PackageDexOptimizer#DEX_OPT_CANCELLED} - * {@link PackageDexOptimizer#DEX_OPT_PERFORMED} - * {@link PackageDexOptimizer#DEX_OPT_FAILED} - */ - @DexOptResult - private int trackPerformDexOpt(String pkg, boolean isForPrimaryDex, - ThrowingCheckedSupplier<Integer, LegacyDexoptDisabledException> performDexOptWrapper) - throws LegacyDexoptDisabledException { - ArraySet<String> failedPackageNames; - synchronized (mLock) { - failedPackageNames = - isForPrimaryDex ? mFailedPackageNamesPrimary : mFailedPackageNamesSecondary; - if (failedPackageNames.contains(pkg)) { - // Skip previously failing package - return PackageDexOptimizer.DEX_OPT_SKIPPED; - } - } - int result = performDexOptWrapper.get(); - if (result == PackageDexOptimizer.DEX_OPT_FAILED) { - synchronized (mLock) { - failedPackageNames.add(pkg); - } - } else if (result == PackageDexOptimizer.DEX_OPT_CANCELLED) { - synchronized (mLock) { - mLastCancelledPackages.add(pkg); - } - } - return result; - } - - @Status - private int convertPackageDexOptimizerStatusToInternal(@DexOptResult int pdoStatus) { - switch (pdoStatus) { - case PackageDexOptimizer.DEX_OPT_CANCELLED: - return STATUS_ABORT_BY_CANCELLATION; - case PackageDexOptimizer.DEX_OPT_FAILED: - return STATUS_DEX_OPT_FAILED; - case PackageDexOptimizer.DEX_OPT_PERFORMED: - case PackageDexOptimizer.DEX_OPT_SKIPPED: - return STATUS_OK; - default: - Slog.e(TAG, "Unkknown error code from PackageDexOptimizer:" + pdoStatus, - new RuntimeException()); - return STATUS_DEX_OPT_FAILED; - } - } - - /** Evaluate whether or not idle optimizations should continue. */ - @Status - private int abortIdleOptimizations(long lowStorageThreshold) { - if (isCancelling()) { - // JobScheduler requested an early abort. - return STATUS_ABORT_BY_CANCELLATION; - } - - // Abort background dexopt if the device is in a moderate or stronger thermal throttling - // state. - int thermalStatus = mInjector.getCurrentThermalStatus(); - if (DEBUG) { - Log.d(TAG, "Thermal throttling status during bgdexopt: " + thermalStatus); - } - if (thermalStatus >= mThermalStatusCutoff) { - return STATUS_ABORT_THERMAL; - } - - if (mInjector.isBatteryLevelLow()) { - return STATUS_ABORT_BATTERY; - } - - long usableSpace = mInjector.getDataDirUsableSpace(); - if (usableSpace < lowStorageThreshold) { - // Rather bail than completely fill up the disk. - Slog.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace); - return STATUS_ABORT_NO_SPACE_LEFT; - } - - return STATUS_OK; - } - - // Evaluate whether apps should be downgraded. - private boolean shouldDowngrade(long lowStorageThresholdForDowngrade) { - if (mInjector.getDataDirUsableSpace() < lowStorageThresholdForDowngrade) { - return true; - } - - return false; - } - - private boolean isCancelling() { - synchronized (mLock) { - return mDexOptCancellingThread != null; - } - } - - private void markPostBootUpdateCompleted(JobParameters params) { - if (params.getJobId() != JOB_POST_BOOT_UPDATE) { - return; - } - synchronized (mLock) { - if (!mFinishedPostBootUpdate) { - mFinishedPostBootUpdate = true; - } - } - // Safe to do this outside lock. - mInjector.getJobScheduler().cancel(JOB_POST_BOOT_UPDATE); - } - - private void notifyPinService(ArraySet<String> updatedPackages) { - PinnerService pinnerService = mInjector.getPinnerService(); - if (pinnerService != null) { - Slog.i(TAG, "Pinning optimized code " + updatedPackages); - pinnerService.update(updatedPackages, false /* force */); - } - } - - /** Notify all listeners (#addPackagesUpdatedListener) that packages have been updated. */ - private void notifyPackagesUpdated(ArraySet<String> updatedPackages) { - synchronized (mLock) { - for (PackagesUpdatedListener listener : mPackagesUpdatedListeners) { - listener.onPackagesUpdated(updatedPackages); - } - } - } - - private void writeStatsLog(JobParameters params) { - @Status int status; - long durationMs; - long durationIncludingSleepMs; - synchronized (mLock) { - status = mLastExecutionStatus; - durationMs = mLastExecutionDurationMs; - } - - mStatsLogger.write(status, params.getStopReason(), durationMs); - } - - /** Injector pattern for testing purpose */ - @VisibleForTesting - static final class Injector { - private final Context mContext; - private final DexManager mDexManager; - private final PackageManagerService mPackageManagerService; - private final File mDataDir = Environment.getDataDirectory(); - - Injector(Context context, DexManager dexManager, PackageManagerService pm) { - mContext = context; - mDexManager = dexManager; - mPackageManagerService = pm; - } - - int getCallingUid() { - return Binder.getCallingUid(); - } - - Context getContext() { - return mContext; - } - - PackageManagerService getPackageManagerService() { - return mPackageManagerService; - } - - DexOptHelper getDexOptHelper() { - return new DexOptHelper(getPackageManagerService()); - } - - JobScheduler getJobScheduler() { - return mContext.getSystemService(JobScheduler.class); - } - - DexManager getDexManager() { - return mDexManager; - } - - PinnerService getPinnerService() { - return LocalServices.getService(PinnerService.class); - } - - boolean isBackgroundDexOptDisabled() { - return SystemProperties.getBoolean( - "pm.dexopt.disable_bg_dexopt" /* key */, false /* default */); - } - - boolean isBatteryLevelLow() { - return LocalServices.getService(BatteryManagerInternal.class).getBatteryLevelLow(); - } - - long getDowngradeUnusedAppsThresholdInMillis() { - String sysPropKey = "pm.dexopt.downgrade_after_inactive_days"; - String sysPropValue = SystemProperties.get(sysPropKey); - if (sysPropValue == null || sysPropValue.isEmpty()) { - Slog.w(TAG, "SysProp " + sysPropKey + " not set"); - return Long.MAX_VALUE; - } - return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue)); - } - - boolean supportSecondaryDex() { - return (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)); - } - - long getDataDirUsableSpace() { - return mDataDir.getUsableSpace(); - } - - long getDataDirStorageLowBytes() { - return mContext.getSystemService(StorageManager.class).getStorageLowBytes(mDataDir); - } - - int getCurrentThermalStatus() { - IThermalService thermalService = IThermalService.Stub.asInterface( - ServiceManager.getService(Context.THERMAL_SERVICE)); - try { - return thermalService.getCurrentThermalStatus(); - } catch (RemoteException e) { - return STATUS_ABORT_THERMAL; - } - } - - int getDexOptThermalCutoff() { - return SystemProperties.getInt( - "dalvik.vm.dexopt.thermal-cutoff", THERMAL_CUTOFF_DEFAULT); - } - - Thread createAndStartThread(String name, Runnable target) { - Thread thread = new Thread(target, name); - Slog.i(TAG, "Starting thread:" + name); - thread.start(); - return thread; - } - } -} diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index b5476fdd3050..9480c8e72402 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -137,7 +137,6 @@ import com.android.internal.util.CollectionUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.modules.utils.TypedXmlSerializer; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.parsing.PackageInfoUtils; @@ -419,7 +418,6 @@ public class ComputerEngine implements Computer { private final PackageDexOptimizer mPackageDexOptimizer; private final DexManager mDexManager; private final CompilerStats mCompilerStats; - private final BackgroundDexOptService mBackgroundDexOptService; private final PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy; private final CrossProfileIntentResolverEngine mCrossProfileIntentResolverEngine; @@ -472,7 +470,6 @@ public class ComputerEngine implements Computer { mPackageDexOptimizer = args.service.mPackageDexOptimizer; mDexManager = args.service.getDexManager(); mCompilerStats = args.service.mCompilerStats; - mBackgroundDexOptService = args.service.mBackgroundDexOptService; mExternalSourcesPolicy = args.service.mExternalSourcesPolicy; mCrossProfileIntentResolverEngine = new CrossProfileIntentResolverEngine( mUserManager, mDomainVerificationManager, mDefaultAppProvider, mContext); @@ -3093,40 +3090,7 @@ public class ComputerEngine implements Computer { } ipw.println("Dexopt state:"); ipw.increaseIndent(); - if (DexOptHelper.useArtService()) { - DexOptHelper.dumpDexoptState(ipw, packageName); - } else { - Collection<? extends PackageStateInternal> pkgSettings; - if (setting != null) { - pkgSettings = Collections.singletonList(setting); - } else { - pkgSettings = mSettings.getPackages().values(); - } - - for (PackageStateInternal pkgSetting : pkgSettings) { - final AndroidPackage pkg = pkgSetting.getPkg(); - if (pkg == null || pkg.isApex()) { - // Skip APEX which is not dex-optimized - continue; - } - final String pkgName = pkg.getPackageName(); - ipw.println("[" + pkgName + "]"); - ipw.increaseIndent(); - - // TODO(b/251903639): Call into ART Service. - try { - mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting, - mDexManager.getPackageUseInfoOrDefault(pkgName)); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - ipw.decreaseIndent(); - } - ipw.println("BgDexopt state:"); - ipw.increaseIndent(); - mBackgroundDexOptService.dump(ipw); - ipw.decreaseIndent(); - } + DexOptHelper.dumpDexoptState(ipw, packageName); ipw.decreaseIndent(); break; } diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index ecfc768a874e..51793f65f7a1 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -23,7 +23,6 @@ import static android.os.incremental.IncrementalManager.isIncrementalPath; import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; import static com.android.server.pm.ApexManager.ActiveApexInfo; -import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.PackageManagerService.REASON_BOOT_AFTER_MAINLINE_UPDATE; @@ -32,10 +31,7 @@ import static com.android.server.pm.PackageManagerService.REASON_CMDLINE; import static com.android.server.pm.PackageManagerService.REASON_FIRST_BOOT; import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX; import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; -import static com.android.server.pm.PackageManagerService.STUB_SUFFIX; import static com.android.server.pm.PackageManagerService.TAG; -import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; -import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter; import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_APEX_PKG; import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PKG; import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal; @@ -45,7 +41,6 @@ import static dalvik.system.DexFile.isProfileGuidedCompilerFilter; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; -import android.app.role.RoleManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -56,8 +51,6 @@ import android.content.pm.IPackageManagerNative; import android.content.pm.IStagedApexObserver; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.pm.SharedLibraryInfo; -import android.content.pm.dex.ArtManager; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; @@ -83,8 +76,6 @@ import com.android.server.art.ReasonMapping; import com.android.server.art.model.ArtFlags; import com.android.server.art.model.DexoptParams; import com.android.server.art.model.DexoptResult; -import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.PackageDexOptimizer.DexOptResult; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; @@ -131,228 +122,6 @@ public final class DexOptHelper { } /** - * Performs dexopt on the set of packages in {@code packages} and returns an int array - * containing statistics about the invocation. The array consists of three elements, - * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} - * and {@code numberOfPackagesFailed}. - */ - public int[] performDexOptUpgrade(List<PackageStateInternal> packageStates, - final int compilationReason, boolean bootComplete) - throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - int numberOfPackagesVisited = 0; - int numberOfPackagesOptimized = 0; - int numberOfPackagesSkipped = 0; - int numberOfPackagesFailed = 0; - final int numberOfPackagesToDexopt = packageStates.size(); - - for (var packageState : packageStates) { - var pkg = packageState.getAndroidPackage(); - numberOfPackagesVisited++; - - boolean useProfileForDexopt = false; - - if ((mPm.isFirstBoot() || mPm.isDeviceUpgrading()) && packageState.isSystem()) { - // Copy over initial preopt profiles since we won't get any JIT samples for methods - // that are already compiled. - File profileFile = new File(getPrebuildProfilePath(pkg)); - // Copy profile if it exists. - if (profileFile.exists()) { - try { - // We could also do this lazily before calling dexopt in - // PackageDexOptimizer to prevent this happening on first boot. The issue - // is that we don't have a good way to say "do this only once". - if (!mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(), - pkg.getUid(), pkg.getPackageName(), - ArtManager.getProfileName(null))) { - Log.e(TAG, "Installer failed to copy system profile!"); - } else { - // Disabled as this causes speed-profile compilation during first boot - // even if things are already compiled. - // useProfileForDexopt = true; - } - } catch (InstallerException | RuntimeException e) { - Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", - e); - } - } else { - PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr( - pkg.getPackageName()); - // Handle compressed APKs in this path. Only do this for stubs with profiles to - // minimize the number off apps being speed-profile compiled during first boot. - // The other paths will not change the filter. - if (disabledPs != null && disabledPs.getPkg().isStub()) { - // The package is the stub one, remove the stub suffix to get the normal - // package and APK names. - String systemProfilePath = getPrebuildProfilePath(disabledPs.getPkg()) - .replace(STUB_SUFFIX, ""); - profileFile = new File(systemProfilePath); - // If we have a profile for a compressed APK, copy it to the reference - // location. - // Note that copying the profile here will cause it to override the - // reference profile every OTA even though the existing reference profile - // may have more data. We can't copy during decompression since the - // directories are not set up at that point. - if (profileFile.exists()) { - try { - // We could also do this lazily before calling dexopt in - // PackageDexOptimizer to prevent this happening on first boot. The - // issue is that we don't have a good way to say "do this only - // once". - if (!mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(), - pkg.getUid(), pkg.getPackageName(), - ArtManager.getProfileName(null))) { - Log.e(TAG, "Failed to copy system profile for stub package!"); - } else { - useProfileForDexopt = true; - } - } catch (InstallerException | RuntimeException e) { - Log.e(TAG, "Failed to copy profile " - + profileFile.getAbsolutePath() + " ", e); - } - } - } - } - } - - if (!mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName()); - } - numberOfPackagesSkipped++; - continue; - } - - if (DEBUG_DEXOPT) { - Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " - + numberOfPackagesToDexopt + ": " + pkg.getPackageName()); - } - - int pkgCompilationReason = compilationReason; - if (useProfileForDexopt) { - // Use background dexopt mode to try and use the profile. Note that this does not - // guarantee usage of the profile. - pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; - } - - int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; - - String filter = getCompilerFilterForReason(pkgCompilationReason); - if (isProfileGuidedCompilerFilter(filter)) { - // DEXOPT_CHECK_FOR_PROFILES_UPDATES used to be false to avoid merging profiles - // during boot which might interfere with background compilation (b/28612421). - // However those problems were related to the verify-profile compiler filter which - // doesn't exist any more, so enable it again. - dexoptFlags |= DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES; - } - - if (compilationReason == REASON_FIRST_BOOT) { - // TODO: This doesn't cover the upgrade case, we should check for this too. - dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; - } - int primaryDexOptStatus = performDexOptTraced( - new DexoptOptions(pkg.getPackageName(), pkgCompilationReason, filter, - /*splitName*/ null, dexoptFlags)); - - switch (primaryDexOptStatus) { - case PackageDexOptimizer.DEX_OPT_PERFORMED: - numberOfPackagesOptimized++; - break; - case PackageDexOptimizer.DEX_OPT_SKIPPED: - numberOfPackagesSkipped++; - break; - case PackageDexOptimizer.DEX_OPT_CANCELLED: - // ignore this case - break; - case PackageDexOptimizer.DEX_OPT_FAILED: - numberOfPackagesFailed++; - break; - default: - Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStatus); - break; - } - } - - return new int[]{numberOfPackagesOptimized, numberOfPackagesSkipped, - numberOfPackagesFailed}; - } - - /** - * Checks if system UI package (typically "com.android.systemui") needs to be re-compiled, and - * compiles it if needed. - */ - private void checkAndDexOptSystemUi(int reason) throws LegacyDexoptDisabledException { - Computer snapshot = mPm.snapshotComputer(); - String sysUiPackageName = - mPm.mContext.getString(com.android.internal.R.string.config_systemUi); - AndroidPackage pkg = snapshot.getPackage(sysUiPackageName); - if (pkg == null) { - Log.w(TAG, "System UI package " + sysUiPackageName + " is not found for dexopting"); - return; - } - - String defaultCompilerFilter = getCompilerFilterForReason(reason); - String targetCompilerFilter = - SystemProperties.get("dalvik.vm.systemuicompilerfilter", defaultCompilerFilter); - String compilerFilter; - - if (isProfileGuidedCompilerFilter(targetCompilerFilter)) { - compilerFilter = "verify"; - File profileFile = new File(getPrebuildProfilePath(pkg)); - - // Copy the profile to the reference profile path if it exists. Installd can only use a - // profile at the reference profile path for dexopt. - if (profileFile.exists()) { - try { - synchronized (mPm.mInstallLock) { - if (mPm.mInstaller.copySystemProfile(profileFile.getAbsolutePath(), - pkg.getUid(), pkg.getPackageName(), - ArtManager.getProfileName(null))) { - compilerFilter = targetCompilerFilter; - } else { - Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath()); - } - } - } catch (InstallerException | RuntimeException e) { - Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath(), e); - } - } - } else { - compilerFilter = targetCompilerFilter; - } - - performDexoptPackage(sysUiPackageName, reason, compilerFilter); - } - - private void dexoptLauncher(int reason) throws LegacyDexoptDisabledException { - Computer snapshot = mPm.snapshotComputer(); - RoleManager roleManager = mPm.mContext.getSystemService(RoleManager.class); - for (var packageName : roleManager.getRoleHolders(RoleManager.ROLE_HOME)) { - AndroidPackage pkg = snapshot.getPackage(packageName); - if (pkg == null) { - Log.w(TAG, "Launcher package " + packageName + " is not found for dexopting"); - } else { - performDexoptPackage(packageName, reason, "speed-profile"); - } - } - } - - private void performDexoptPackage(@NonNull String packageName, int reason, - @NonNull String compilerFilter) throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - - // DEXOPT_CHECK_FOR_PROFILES_UPDATES is set to replicate behaviour that will be - // unconditionally enabled for profile guided filters when ART Service is called instead of - // the legacy PackageDexOptimizer implementation. - int dexoptFlags = isProfileGuidedCompilerFilter(compilerFilter) - ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES - : 0; - - performDexOptTraced(new DexoptOptions( - packageName, reason, compilerFilter, null /* splitName */, dexoptFlags)); - } - - /** * Called during startup to do any boot time dexopting. This can occasionally be time consuming * (30+ seconds) and the function will block until it is complete. */ @@ -377,35 +146,9 @@ public final class DexOptHelper { final long startTime = System.nanoTime(); - if (useArtService()) { - mBootDexoptStartTime = startTime; - getArtManagerLocal().onBoot(DexoptOptions.convertToArtServiceDexoptReason(reason), - null /* progressCallbackExecutor */, null /* progressCallback */); - } else { - try { - // System UI and the launcher are important to user experience, so we check them - // after a mainline update or OTA. They may need to be re-compiled in these cases. - checkAndDexOptSystemUi(reason); - dexoptLauncher(reason); - - if (reason != REASON_BOOT_AFTER_OTA && reason != REASON_FIRST_BOOT) { - return; - } - - final Computer snapshot = mPm.snapshotComputer(); - - // TODO(b/251903639): Align this with how ART Service selects packages for boot - // compilation. - List<PackageStateInternal> pkgSettings = - getPackagesForDexopt(snapshot.getPackageStates().values(), mPm); - - final int[] stats = - performDexOptUpgrade(pkgSettings, reason, false /* bootComplete */); - reportBootDexopt(startTime, stats[0], stats[1], stats[2]); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } + mBootDexoptStartTime = startTime; + getArtManagerLocal().onBoot(DexoptOptions.convertToArtServiceDexoptReason(reason), + null /* progressCallbackExecutor */, null /* progressCallback */); } private void reportBootDexopt(long startTime, int numDexopted, int numSkipped, int numFailed) { @@ -450,15 +193,7 @@ public final class DexOptHelper { @DexOptResult int dexoptStatus; if (options.isDexoptOnlySecondaryDex()) { - if (useArtService()) { - dexoptStatus = performDexOptWithArtService(options, 0 /* extraFlags */); - } else { - try { - return mPm.getDexManager().dexoptSecondaryDex(options); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } + dexoptStatus = performDexOptWithArtService(options, 0 /* extraFlags */); } else { dexoptStatus = performDexOptWithStatus(options); } @@ -491,39 +226,11 @@ public final class DexOptHelper { // if the package can now be considered up to date for the given filter. @DexOptResult private int performDexOptInternal(DexoptOptions options) { - if (useArtService()) { - return performDexOptWithArtService(options, ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES); - } - - AndroidPackage p; - PackageSetting pkgSetting; - synchronized (mPm.mLock) { - p = mPm.mPackages.get(options.getPackageName()); - pkgSetting = mPm.mSettings.getPackageLPr(options.getPackageName()); - if (p == null || pkgSetting == null) { - // Package could not be found. Report failure. - return PackageDexOptimizer.DEX_OPT_FAILED; - } - if (p.isApex()) { - // APEX needs no dexopt - return PackageDexOptimizer.DEX_OPT_SKIPPED; - } - mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked()); - mPm.mCompilerStats.maybeWriteAsync(); - } - final long callingId = Binder.clearCallingIdentity(); - try { - return performDexOptInternalWithDependenciesLI(p, pkgSetting, options); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } finally { - Binder.restoreCallingIdentity(callingId); - } + return performDexOptWithArtService(options, ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES); } /** - * Performs dexopt on the given package using ART Service. May only be called when ART Service - * is enabled, i.e. when {@link useArtService} returns true. + * Performs dexopt on the given package using ART Service. */ @DexOptResult private int performDexOptWithArtService(DexoptOptions options, @@ -545,91 +252,6 @@ public final class DexOptHelper { } } - @DexOptResult - private int performDexOptInternalWithDependenciesLI( - AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options) - throws LegacyDexoptDisabledException { - if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) { - // This needs to be done in odrefresh in early boot, for security reasons. - throw new IllegalArgumentException("Cannot dexopt the system server"); - } - - // Select the dex optimizer based on the force parameter. - // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to - // allocate an object here. - PackageDexOptimizer pdo = options.isForce() - ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPm.mPackageDexOptimizer) - : mPm.mPackageDexOptimizer; - - // Dexopt all dependencies first. Note: we ignore the return value and march on - // on errors. - // Note that we are going to call performDexOpt on those libraries as many times as - // they are referenced in packages. When we do a batch of performDexOpt (for example - // at boot, or background job), the passed 'targetCompilerFilter' stays the same, - // and the first package that uses the library will dexopt it. The - // others will see that the compiled code for the library is up to date. - Collection<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting); - final String[] instructionSets = getAppDexInstructionSets( - pkgSetting.getPrimaryCpuAbi(), - pkgSetting.getSecondaryCpuAbi()); - if (!deps.isEmpty()) { - DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), - options.getCompilationReason(), options.getCompilerFilter(), - options.getSplitName(), - options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); - for (SharedLibraryInfo info : deps) { - Computer snapshot = mPm.snapshotComputer(); - AndroidPackage depPackage = snapshot.getPackage(info.getPackageName()); - PackageStateInternal depPackageStateInternal = - snapshot.getPackageStateInternal(info.getPackageName()); - if (depPackage != null && depPackageStateInternal != null) { - // TODO: Analyze and investigate if we (should) profile libraries. - pdo.performDexOpt(depPackage, depPackageStateInternal, instructionSets, - mPm.getOrCreateCompilerPackageStats(depPackage), - mPm.getDexManager().getPackageUseInfoOrDefault( - depPackage.getPackageName()), libraryOptions); - } else { - // TODO(ngeoffray): Support dexopting system shared libraries. - } - } - } - - return pdo.performDexOpt(p, pkgSetting, instructionSets, - mPm.getOrCreateCompilerPackageStats(p), - mPm.getDexManager().getPackageUseInfoOrDefault(p.getPackageName()), options); - } - - /** @deprecated For legacy shell command only. */ - @Deprecated - public void forceDexOpt(@NonNull Computer snapshot, String packageName) - throws LegacyDexoptDisabledException { - PackageManagerServiceUtils.enforceSystemOrRoot("forceDexOpt"); - - final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); - final AndroidPackage pkg = packageState == null ? null : packageState.getPkg(); - if (packageState == null || pkg == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - if (pkg.isApex()) { - throw new IllegalArgumentException("Can't dexopt APEX package: " + packageName); - } - - Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt"); - - // Whoever is calling forceDexOpt wants a compiled package. - // Don't use profiles since that may cause compilation to be skipped. - DexoptOptions options = new DexoptOptions(packageName, REASON_CMDLINE, - getDefaultCompilerFilter(), null /* splitName */, - DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE); - - @DexOptResult int res = performDexOptInternalWithDependenciesLI(pkg, packageState, options); - - Trace.traceEnd(TRACE_TAG_DALVIK); - if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { - throw new IllegalStateException("Failed to dexopt: " + res); - } - } - public boolean performDexOptMode(@NonNull Computer snapshot, String packageName, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { if (!PackageManagerServiceUtils.isSystemOrRootOrShell() @@ -872,10 +494,6 @@ public final class DexOptHelper { } } - /*package*/ void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException { - mPm.mPackageDexOptimizer.controlDexOptBlocking(block); - } - /** * Dumps the dexopt state for the given package, or all packages if it is null. */ @@ -935,19 +553,9 @@ public final class DexOptHelper { } /** - * Returns true if ART Service should be used for package optimization. - */ - public static boolean useArtService() { - return SystemProperties.getBoolean("dalvik.vm.useartservice", false); - } - - /** * Returns {@link DexUseManagerLocal} if ART Service should be used for package optimization. */ public static @Nullable DexUseManagerLocal getDexUseManagerLocal() { - if (!useArtService()) { - return null; - } try { return LocalManagerRegistry.getManagerOrThrow(DexUseManagerLocal.class); } catch (ManagerNotFoundException e) { @@ -1039,10 +647,6 @@ public final class DexOptHelper { */ public static void initializeArtManagerLocal( @NonNull Context systemContext, @NonNull PackageManagerService pm) { - if (!useArtService()) { - return; - } - ArtManagerLocal artManager = new ArtManagerLocal(systemContext); artManager.addDexoptDoneCallback(false /* onlyIncludeUpdates */, Runnable::run, pm.getDexOptHelper().new DexoptDoneHandler()); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index ae68018c90b3..c559892327df 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -46,10 +46,7 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_DE; import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.APP_METADATA_FILE_NAME; -import static com.android.server.pm.DexOptHelper.useArtService; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; -import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; -import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; @@ -173,7 +170,6 @@ import com.android.server.EventLogTags; import com.android.server.SystemConfig; import com.android.server.art.model.DexoptResult; import com.android.server.criticalevents.CriticalEventLog; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; @@ -272,8 +268,6 @@ final class InstallPackageHelper { final PackageSetting oldPkgSetting = request.getScanRequestOldPackageSetting(); final PackageSetting originalPkgSetting = request.getScanRequestOriginalPackageSetting(); final String realPkgName = request.getRealPackageName(); - final List<String> changedAbiCodePath = - useArtService() ? null : request.getChangedAbiCodePath(); final PackageSetting pkgSetting; if (request.getScanRequestPackageSetting() != null) { SharedUserSetting requestSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr( @@ -449,23 +443,6 @@ final class InstallPackageHelper { } pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails); - // The conditional on useArtService() for changedAbiCodePath above means this is skipped - // when ART Service is in use, since it has its own dex file GC. - if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { - for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { - final String codePathString = changedAbiCodePath.get(i); - try { - synchronized (mPm.mInstallLock) { - mPm.mInstaller.rmdex(codePathString, - getDexCodeInstructionSet(getPreferredInstructionSet())); - } - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } catch (Installer.InstallerException ignored) { - } - } - } - final int userId = request.getUserId(); // Modify state for the given package setting commitPackageSettings(pkg, pkgSetting, oldPkgSetting, reconciledPkg); @@ -2538,20 +2515,6 @@ final class InstallPackageHelper { pkg.getBaseApkPath(), pkg.getSplitCodePaths()); } - // ART Service handles this on demand instead. - if (!useArtService() && pkg != null) { - // Prepare the application profiles for the new code paths. - // This needs to be done before invoking dexopt so that any install-time profile - // can be used for optimizations. - try { - mArtManagerService.prepareAppProfiles(pkg, - mPm.resolveUserIds(installRequest.getUserId()), - /* updateReferenceProfileContent= */ true); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } - // Construct the DexoptOptions early to see if we should skip running dexopt. // // Do not run PackageDexOptimizer through the local performDexOpt @@ -2602,36 +2565,11 @@ final class InstallPackageHelper { realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); - if (useArtService()) { - DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService( - installRequest, dexoptOptions); - installRequest.onDexoptFinished(dexOptResult); - } else { - try { - mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting, - null /* instructionSets */, - mPm.getOrCreateCompilerPackageStats(pkg), - mDexManager.getPackageUseInfoOrDefault(packageName), dexoptOptions); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } + DexoptResult dexOptResult = + DexOptHelper.dexoptPackageUsingArtService(installRequest, dexoptOptions); + installRequest.onDexoptFinished(dexOptResult); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - - if (!useArtService()) { - // Notify BackgroundDexOptService that the package has been changed. - // If this is an update of a package which used to fail to compile, - // BackgroundDexOptService will remove it from its denylist. - // ART Service currently doesn't support this and will retry packages in every - // background dexopt. - // TODO: Layering violation - try { - BackgroundDexOptService.getService().notifyPackageChanged(packageName); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } } PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental( incrementalStorages); diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 34903d1ed47d..8038c9a8cb30 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -16,8 +16,6 @@ package com.android.server.pm; -import static com.android.server.pm.DexOptHelper.useArtService; - import android.annotation.AppIdInt; import android.annotation.NonNull; import android.annotation.Nullable; @@ -97,15 +95,6 @@ public class Installer extends SystemService { */ public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3; - /** - * The results of {@code getOdexVisibility}. See - * {@link #getOdexVisibility(String, String, String)} for details. - */ - public static final int ODEX_NOT_FOUND = 0; - public static final int ODEX_IS_PUBLIC = 1; - public static final int ODEX_IS_PRIVATE = 2; - - public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE; public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL; @@ -611,37 +600,7 @@ public class Installer extends SystemService { } /** - * Runs dex optimization. - * - * @param apkPath Path of target APK - * @param uid UID of the package - * @param pkgName Name of the package - * @param instructionSet Target instruction set to run dex optimization. - * @param dexoptNeeded Necessary dex optimization for this request. Check - * {@link dalvik.system.DexFile#NO_DEXOPT_NEEDED}, - * {@link dalvik.system.DexFile#DEX2OAT_FROM_SCRATCH}, - * {@link dalvik.system.DexFile#DEX2OAT_FOR_BOOT_IMAGE}, and - * {@link dalvik.system.DexFile#DEX2OAT_FOR_FILTER}. - * @param outputPath Output path of generated dex optimization. - * @param dexFlags Check {@code DEXOPT_*} for allowed flags. - * @param compilerFilter Compiler filter like "verify", "speed-profile". Check - * {@code art/libartbase/base/compiler_filter.cc} for full list. - * @param volumeUuid UUID of the volume where the package data is stored. {@code null} - * represents internal storage. - * @param classLoaderContext This encodes the class loader chain (class loader type + class - * path) in a format compatible to dex2oat. Check - * {@code DexoptUtils.processContextForDexLoad} for further details. - * @param seInfo Selinux context to set for generated outputs. - * @param downgrade If set, allows downgrading {@code compilerFilter}. If downgrading is not - * allowed and requested {@code compilerFilter} is considered as downgrade, - * the request will be ignored. - * @param targetSdkVersion Target SDK version of the package. - * @param profileName Name of reference profile file. - * @param dexMetadataPath Specifies the location of dex metadata file. - * @param compilationReason Specifies the reason for the compilation like "install". - * @return {@code true} if {@code dexopt} is completed. {@code false} if it was cancelled. - * - * @throws InstallerException if {@code dexopt} fails. + * This function only remains to allow overriding in OtaDexoptService. */ public boolean dexopt(String apkPath, int uid, String pkgName, String instructionSet, int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter, @@ -650,98 +609,7 @@ public class Installer extends SystemService { @Nullable String profileName, @Nullable String dexMetadataPath, @Nullable String compilationReason) throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - assertValidInstructionSet(instructionSet); - BlockGuard.getVmPolicy().onPathAccess(apkPath); - BlockGuard.getVmPolicy().onPathAccess(outputPath); - BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath); - if (!checkBeforeRemote()) return false; - try { - return mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath, - dexFlags, compilerFilter, volumeUuid, classLoaderContext, seInfo, downgrade, - targetSdkVersion, profileName, dexMetadataPath, compilationReason); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - /** - * Enables or disables dex optimization blocking. - * - * <p> Enabling blocking will also involve cancelling pending dexopt call and killing child - * processes forked from installd to run dexopt. The pending dexopt call will return false - * when it is cancelled. - * - * @param block set to true to enable blocking / false to disable blocking. - */ - public void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - try { - mInstalld.controlDexOptBlocking(block); - } catch (Exception e) { - Slog.w(TAG, "blockDexOpt failed", e); - } - } - - /** - * Analyzes the ART profiles of the given package, possibly merging the information - * into the reference profile. Returns whether or not we should optimize the package - * based on how much information is in the profile. - * - * @return one of {@link #PROFILE_ANALYSIS_OPTIMIZE}, - * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA}, - * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES} - */ - public int mergeProfiles(int uid, String packageName, String profileName) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; - try { - return mInstalld.mergeProfiles(uid, packageName, profileName); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - /** - * Dumps profiles associated with a package in a human readable format. - */ - public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath, - boolean dumpClassesAndMethods) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return false; - BlockGuard.getVmPolicy().onPathAccess(codePath); - try { - return mInstalld.dumpProfiles(uid, packageName, profileName, codePath, - dumpClassesAndMethods); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - public boolean copySystemProfile(String systemProfile, int uid, String packageName, - String profileName) throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return false; - try { - return mInstalld.copySystemProfile(systemProfile, uid, packageName, profileName); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - public void rmdex(String codePath, String instructionSet) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - assertValidInstructionSet(instructionSet); - if (!checkBeforeRemote()) return; - BlockGuard.getVmPolicy().onPathAccess(codePath); - try { - mInstalld.rmdex(codePath, instructionSet); - } catch (Exception e) { - throw InstallerException.from(e); - } + throw new LegacyDexoptDisabledException(); } /** @@ -757,43 +625,6 @@ public class Installer extends SystemService { } } - public void clearAppProfiles(String packageName, String profileName) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return; - try { - mInstalld.clearAppProfiles(packageName, profileName); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - public void destroyAppProfiles(String packageName) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return; - try { - mInstalld.destroyAppProfiles(packageName); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - /** - * Deletes the reference profile with the given name of the given package. - * @throws InstallerException if the deletion fails. - */ - public void deleteReferenceProfile(String packageName, String profileName) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return; - try { - mInstalld.deleteReferenceProfile(packageName, profileName); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - public void createUserData(String uuid, int userId, int userSerial, int flags) throws InstallerException { if (!checkBeforeRemote()) return; @@ -889,40 +720,6 @@ public class Installer extends SystemService { } } - /** - * Deletes the optimized artifacts generated by ART and returns the number - * of freed bytes. - */ - public long deleteOdex(String packageName, String apkPath, String instructionSet, - String outputPath) throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return -1; - BlockGuard.getVmPolicy().onPathAccess(apkPath); - BlockGuard.getVmPolicy().onPathAccess(outputPath); - try { - return mInstalld.deleteOdex(packageName, apkPath, instructionSet, outputPath); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid, - String[] isas, @Nullable String volumeUuid, int flags) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - for (int i = 0; i < isas.length; i++) { - assertValidInstructionSet(isas[i]); - } - if (!checkBeforeRemote()) return false; - BlockGuard.getVmPolicy().onPathAccess(apkPath); - try { - return mInstalld.reconcileSecondaryDexFile(apkPath, packageName, uid, isas, - volumeUuid, flags); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid, @Nullable String volumeUuid, int flags) throws InstallerException { if (!checkBeforeRemote()) return new byte[0]; @@ -934,28 +731,6 @@ public class Installer extends SystemService { } } - public boolean createProfileSnapshot(int appId, String packageName, String profileName, - String classpath) throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return false; - try { - return mInstalld.createProfileSnapshot(appId, packageName, profileName, classpath); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - public void destroyProfileSnapshot(String packageName, String profileName) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return; - try { - mInstalld.destroyProfileSnapshot(packageName, profileName); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - public void invalidateMounts() throws InstallerException { if (!checkBeforeRemote()) return; try { @@ -999,30 +774,6 @@ public class Installer extends SystemService { } /** - * Prepares the app profile for the package at the given path: - * <ul> - * <li>Creates the current profile for the given user ID, unless the user ID is - * {@code UserHandle.USER_NULL}.</li> - * <li>Merges the profile from the dex metadata file (if present) into the reference - * profile.</li> - * </ul> - */ - public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId, - String profileName, String codePath, String dexMetadataPath) - throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return false; - BlockGuard.getVmPolicy().onPathAccess(codePath); - BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath); - try { - return mInstalld.prepareAppProfile(pkg, userId, appId, profileName, codePath, - dexMetadataPath); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - /** * Snapshots user data of the given package. * * @param pkg name of the package to snapshot user data for. @@ -1152,34 +903,6 @@ public class Installer extends SystemService { } /** - * Returns the visibility of the optimized artifacts. - * - * @param packageName name of the package. - * @param apkPath path to the APK. - * @param instructionSet instruction set of the optimized artifacts. - * @param outputPath path to the directory that contains the optimized artifacts (i.e., the - * directory that {@link #dexopt} outputs to). - * - * @return {@link #ODEX_NOT_FOUND} if the optimized artifacts are not found, or - * {@link #ODEX_IS_PUBLIC} if the optimized artifacts are accessible by all apps, or - * {@link #ODEX_IS_PRIVATE} if the optimized artifacts are only accessible by this app. - * - * @throws InstallerException if failed to get the visibility of the optimized artifacts. - */ - public int getOdexVisibility(String packageName, String apkPath, String instructionSet, - String outputPath) throws InstallerException, LegacyDexoptDisabledException { - checkLegacyDexoptDisabled(); - if (!checkBeforeRemote()) return -1; - BlockGuard.getVmPolicy().onPathAccess(apkPath); - BlockGuard.getVmPolicy().onPathAccess(outputPath); - try { - return mInstalld.getOdexVisibility(packageName, apkPath, instructionSet, outputPath); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - - /** * Returns an auth token for the provided writable FD. * * @param authFd a file descriptor to proof that the caller can write to the file. @@ -1247,14 +970,4 @@ public class Installer extends SystemService { super("Invalid call to legacy dexopt method while ART Service is in use."); } } - - /** - * Throws LegacyDexoptDisabledException if ART Service should be used instead of the - * {@link android.os.IInstalld} method that follows this method call. - */ - public static void checkLegacyDexoptDisabled() throws LegacyDexoptDisabledException { - if (useArtService()) { - throw new LegacyDexoptDisabledException(); - } - } } diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index c8bc56ce7dcd..85aee8606bc2 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -11,7 +11,6 @@ per-file StagingManager.java = dariofreni@google.com, ioffe@google.com, olilan@g # dex per-file AbstractStatsBase.java = file:dex/OWNERS -per-file BackgroundDexOptService.java = file:dex/OWNERS per-file CompilerStats.java = file:dex/OWNERS per-file DexOptHelper.java = file:dex/OWNERS per-file DynamicCodeLoggingService.java = file:dex/OWNERS diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index ea082cf77987..5b326fd297cb 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import static com.android.server.pm.DexOptHelper.useArtService; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; @@ -305,13 +304,10 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throws InstallerException { final StringBuilder builder = new StringBuilder(); - if (useArtService()) { - if ((dexFlags & DEXOPT_SECONDARY_DEX) != 0) { - // installd may change the reference profile in place for secondary dex - // files, which isn't safe with the lock free approach in ART Service. - throw new IllegalArgumentException( - "Invalid OTA dexopt call for secondary dex"); - } + if ((dexFlags & DEXOPT_SECONDARY_DEX) != 0) { + // installd may change the reference profile in place for secondary dex + // files, which isn't safe with the lock free approach in ART Service. + throw new IllegalArgumentException("Invalid OTA dexopt call for secondary dex"); } // The current version. For v10, see b/115993344. diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 8a4080ff029d..396fa22393e4 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -18,7 +18,6 @@ package com.android.server.pm; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED; -import static com.android.server.pm.DexOptHelper.useArtService; 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; @@ -53,7 +52,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.dex.ArtManager; import android.content.pm.dex.DexMetadataHelper; -import android.os.FileUtils; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; @@ -67,7 +65,6 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.F2fsUtils; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.apphibernation.AppHibernationManagerInternal; import com.android.server.pm.Installer.InstallerException; @@ -92,7 +89,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Random; /** @@ -130,9 +126,8 @@ public class PackageDexOptimizer { private final Object mInstallLock; /** - * This should be accessed only through {@link #getInstallerLI()} with {@link #mInstallLock} - * or {@link #getInstallerWithoutLock()} without the lock. Check both methods for further - * details on when to use each of them. + * This should be accessed only through {@link #getInstallerLI()} with + * {@link #mInstallLock}. */ private final Installer mInstaller; @@ -248,15 +243,6 @@ public class PackageDexOptimizer { } /** - * Cancels currently running dex optimization. - */ - void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException { - // This method should not hold mInstallLock as cancelling should be possible while - // the lock is held by other thread running performDexOpt. - getInstallerWithoutLock().controlDexOptBlocking(block); - } - - /** * Performs dexopt on all code paths of the given package. * It assumes the install lock is held. */ @@ -334,7 +320,7 @@ public class PackageDexOptimizer { final boolean isUsedByOtherApps; if (options.isDexoptAsSharedLibrary()) { isUsedByOtherApps = true; - } else if (useArtService()) { + } else { // We get here when collecting dexopt commands in OTA preopt, even when ART Service // is in use. packageUseInfo isn't useful in that case since the legacy dex use // database hasn't been updated. So we'd have to query ART Service instead, but it @@ -342,8 +328,6 @@ public class PackageDexOptimizer { // That means such apps will get preopted wrong, and we'll leave it to a later // background dexopt after reboot instead. isUsedByOtherApps = false; - } else { - isUsedByOtherApps = packageUseInfo.isUsedByOtherApps(path); } String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter()); @@ -439,12 +423,10 @@ public class PackageDexOptimizer { } } } finally { + // ART Service is always enabled, so we should only arrive here + // during OTA preopt, and there should be no cloud profile. if (cloudProfileName != null) { - try { - mInstaller.deleteReferenceProfile(pkg.getPackageName(), cloudProfileName); - } catch (InstallerException e) { - Slog.w(TAG, "Failed to cleanup cloud profile", e); - } + throw new LegacyDexoptDisabledException(); } } } @@ -457,30 +439,15 @@ public class PackageDexOptimizer { * * @return true on success, or false otherwise. */ - @GuardedBy("mInstallLock") private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path, @Nullable String dexMetadataPath) throws LegacyDexoptDisabledException { if (dexMetadataPath != null) { - if (mInstaller.isIsolated()) { - // If the installer is isolated, the two calls to it below will return immediately, - // so this only short-circuits that a bit. We need to do it to avoid the - // LegacyDexoptDisabledException getting thrown first, when we get here during OTA - // preopt and ART Service is enabled. - return true; - } - - try { - // Make sure we don't keep any existing contents. - mInstaller.deleteReferenceProfile(pkg.getPackageName(), profileName); - - final int appId = UserHandle.getAppId(pkg.getUid()); - mInstaller.prepareAppProfile(pkg.getPackageName(), UserHandle.USER_NULL, appId, - profileName, path, dexMetadataPath); - return true; - } catch (InstallerException e) { - Slog.w(TAG, "Failed to prepare cloud profile", e); - return false; + // ART Service is always enabled, so we should only arrive here + // during OTA preopt, i.e. when the installer is isolated. + if (!mInstaller.isIsolated()) { + throw new LegacyDexoptDisabledException(); } + return true; } else { return false; } @@ -554,37 +521,6 @@ public class PackageDexOptimizer { return getReasonName(compilationReason) + annotation; } - /** - * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}. - * - * @return - * DEX_OPT_FAILED if there was any exception during dexopt - * DEX_OPT_PERFORMED if dexopt was performed successfully on the given path. - * NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file - * didn't need an update. That's because at the moment we don't get more than success/failure - * from installd. - * - * TODO(calin): Consider adding return codes to installd dexopt invocation (rather than - * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though - * that seems wasteful. - */ - @DexOptResult - public int dexOptSecondaryDexPath(ApplicationInfo info, String path, - PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) - throws LegacyDexoptDisabledException { - if (info.uid == -1) { - throw new IllegalArgumentException("Dexopt for path " + path + " has invalid uid."); - } - synchronized (mInstallLock) { - final long acquireTime = acquireWakeLockLI(info.uid); - try { - return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options); - } finally { - releaseWakeLockLI(acquireTime); - } - } - } - @GuardedBy("mInstallLock") private long acquireWakeLockLI(final int uid) { // During boot the system doesn't need to instantiate and obtain a wake lock. @@ -618,69 +554,6 @@ public class PackageDexOptimizer { } } - @GuardedBy("mInstallLock") - @DexOptResult - private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, - PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) - throws LegacyDexoptDisabledException { - String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(), - dexUseInfo.isUsedByOtherApps()); - // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags. - // Secondary dex files are currently not compiled at boot. - int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX; - // Check the app storage and add the appropriate flags. - if (info.deviceProtectedDataDir != null && - FileUtils.contains(info.deviceProtectedDataDir, path)) { - dexoptFlags |= DEXOPT_STORAGE_DE; - } else if (info.credentialProtectedDataDir != null && - FileUtils.contains(info.credentialProtectedDataDir, path)) { - dexoptFlags |= DEXOPT_STORAGE_CE; - } else { - Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName); - return DEX_OPT_FAILED; - } - String classLoaderContext = null; - if (dexUseInfo.isUnsupportedClassLoaderContext() - || dexUseInfo.isVariableClassLoaderContext()) { - // If we have an unknown (not yet set), or a variable class loader chain. Just verify - // the dex file. - compilerFilter = "verify"; - } else { - classLoaderContext = dexUseInfo.getClassLoaderContext(); - } - - int reason = options.getCompilationReason(); - Log.d(TAG, "Running dexopt on: " + path - + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas() - + " reason=" + getReasonName(reason) - + " dexoptFlags=" + printDexoptFlags(dexoptFlags) - + " target-filter=" + compilerFilter - + " class-loader-context=" + classLoaderContext); - - try { - for (String isa : dexUseInfo.getLoaderIsas()) { - // Reuse the same dexopt path as for the primary apks. We don't need all the - // arguments as some (dexopNeeded and oatDir) will be computed by installd because - // system server cannot read untrusted app content. - // TODO(calin): maybe add a separate call. - boolean completed = getInstallerLI().dexopt(path, info.uid, info.packageName, - isa, /* dexoptNeeded= */ 0, - /* outputPath= */ null, dexoptFlags, - compilerFilter, info.volumeUuid, classLoaderContext, info.seInfo, - options.isDowngrade(), info.targetSdkVersion, /* profileName= */ null, - /* dexMetadataPath= */ null, getReasonName(reason)); - if (!completed) { - return DEX_OPT_CANCELLED; - } - } - - return DEX_OPT_PERFORMED; - } catch (InstallerException e) { - Slog.w(TAG, "Failed to dexopt", e); - return DEX_OPT_FAILED; - } - } - /** * Adjust the given dexopt-needed value. Can be overridden to influence the decision to * optimize or not (and in what way). @@ -697,59 +570,6 @@ public class PackageDexOptimizer { } /** - * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}. - */ - void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg, - PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo) - throws LegacyDexoptDisabledException { - final String[] instructionSets = getAppDexInstructionSets(pkgSetting.getPrimaryCpuAbi(), - pkgSetting.getSecondaryCpuAbi()); - final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); - - final List<String> paths = AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg); - - for (String path : paths) { - pw.println("path: " + path); - pw.increaseIndent(); - - for (String isa : dexCodeInstructionSets) { - try { - DexFile.OptimizationInfo info = DexFile.getDexFileOptimizationInfo(path, isa); - pw.println(isa + ": [status=" + info.getStatus() - +"] [reason=" + info.getReason() + "]"); - } catch (IOException ioe) { - pw.println(isa + ": [Exception]: " + ioe.getMessage()); - } - } - - if (useInfo.isUsedByOtherApps(path)) { - pw.println("used by other apps: " + useInfo.getLoadingPackages(path)); - } - - Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap(); - - if (!dexUseInfoMap.isEmpty()) { - pw.println("known secondary dex files:"); - pw.increaseIndent(); - for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) { - String dex = e.getKey(); - PackageDexUsage.DexUseInfo dexUseInfo = e.getValue(); - pw.println(dex); - pw.increaseIndent(); - // TODO(calin): get the status of the oat file (needs installd call) - pw.println("class loader context: " + dexUseInfo.getClassLoaderContext()); - if (dexUseInfo.isUsedByOtherApps()) { - pw.println("used by other apps: " + dexUseInfo.getLoadingPackages()); - } - pw.decreaseIndent(); - } - pw.decreaseIndent(); - } - pw.decreaseIndent(); - } - } - - /** * Returns the compiler filter that should be used to optimize the secondary dex. * The target filter will be updated if the package code is used by other apps * or if it has the safe mode flag set. @@ -898,14 +718,13 @@ public class PackageDexOptimizer { * Assesses if there's a need to perform dexopt on {@code path} for the given * configuration (isa, compiler filter, profile). */ - @GuardedBy("mInstallLock") private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter, String classLoaderContext, int profileAnalysisResult, boolean downgrade, int dexoptFlags, String oatDir) throws LegacyDexoptDisabledException { // Allow calls from OtaDexoptService even when ART Service is in use. The installer is // isolated in that case so later calls to it won't call into installd anyway. if (!mInstaller.isIsolated()) { - Installer.checkLegacyDexoptDisabled(); + throw new LegacyDexoptDisabledException(); } final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0; @@ -953,16 +772,9 @@ public class PackageDexOptimizer { } /** Returns true if the current artifacts of the app are private to the app itself. */ - @GuardedBy("mInstallLock") private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir) throws LegacyDexoptDisabledException { - try { - return mInstaller.getOdexVisibility(packageName, path, isa, oatDir) - == Installer.ODEX_IS_PRIVATE; - } catch (InstallerException e) { - Slog.w(TAG, "Failed to get odex visibility for " + path, e); - return false; - } + throw new LegacyDexoptDisabledException(); } /** @@ -976,22 +788,7 @@ public class PackageDexOptimizer { */ private int analyseProfiles(AndroidPackage pkg, int uid, String profileName, String compilerFilter) throws LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - - // Check if we are allowed to merge and if the compiler filter is profile guided. - if (!isProfileGuidedCompilerFilter(compilerFilter)) { - return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; - } - // Merge profiles. It returns whether or not there was an updated in the profile info. - try { - synchronized (mInstallLock) { - return getInstallerLI().mergeProfiles(uid, pkg.getPackageName(), profileName); - } - } catch (InstallerException e) { - Slog.w(TAG, "Failed to merge profiles", e); - // We don't need to optimize if we failed to merge. - return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; - } + throw new LegacyDexoptDisabledException(); } /** @@ -1101,7 +898,7 @@ public class PackageDexOptimizer { /** * Returns {@link #mInstaller} with {@link #mInstallLock}. This should be used for all - * {@link #mInstaller} access unless {@link #getInstallerWithoutLock()} is allowed. + * {@link #mInstaller} access. */ @GuardedBy("mInstallLock") private Installer getInstallerLI() { @@ -1109,14 +906,6 @@ public class PackageDexOptimizer { } /** - * Returns {@link #mInstaller} without lock. This should be used only inside - * {@link #controlDexOptBlocking(boolean)}. - */ - private Installer getInstallerWithoutLock() { - return mInstaller; - } - - /** * Injector for {@link PackageDexOptimizer} dependencies */ interface Injector { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 35cb5b000219..d215822d1b1c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -41,9 +41,6 @@ import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME; -import static com.android.server.pm.DexOptHelper.useArtService; -import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; -import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; import static com.android.server.pm.PackageManagerServiceUtils.isInstalledByAdb; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; @@ -216,10 +213,8 @@ import com.android.server.art.model.DeleteResult; import com.android.server.compat.CompatChange; import com.android.server.compat.PlatformCompat; import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.Settings.VersionInfo; import com.android.server.pm.dex.ArtManagerService; -import com.android.server.pm.dex.ArtUtils; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DynamicCodeLogger; import com.android.server.pm.local.PackageManagerLocalImpl; @@ -820,8 +815,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService // TODO(b/260124949): Remove these. final PackageDexOptimizer mPackageDexOptimizer; - @Nullable - final BackgroundDexOptService mBackgroundDexOptService; // null when ART Service is in use. // DexManager handles the usage of dex files (e.g. secondary files, whether or not a package // is used by other apps). private final DexManager mDexManager; @@ -1763,16 +1756,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService new DefaultSystemWrapper(), LocalServices::getService, context::getSystemService, - (i, pm) -> { - if (useArtService()) { - return null; - } - try { - return new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - }, (i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService( Context.BACKUP_SERVICE)), (i, pm) -> new SharedLibrariesImpl(pm, i), @@ -1916,7 +1899,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService mApexManager = testParams.apexManager; mArtManagerService = testParams.artManagerService; mAvailableFeatures = testParams.availableFeatures; - mBackgroundDexOptService = testParams.backgroundDexOptService; mDefParseFlags = testParams.defParseFlags; mDefaultAppProvider = testParams.defaultAppProvider; mLegacyPermissionManager = testParams.legacyPermissionManagerInternal; @@ -2113,7 +2095,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService mPackageDexOptimizer = injector.getPackageDexOptimizer(); mDexManager = injector.getDexManager(); mDynamicCodeLogger = injector.getDynamicCodeLogger(); - mBackgroundDexOptService = injector.getBackgroundDexOptService(); mArtManagerService = injector.getArtManagerService(); mMoveCallbacks = new MovePackageHelper.MoveCallbacks(FgThread.get().getLooper()); mSharedLibraries = mInjector.getSharedLibrariesImpl(); @@ -2369,19 +2350,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService null /*scannedPackage*/, mInjector.getAbiHelper().getAdjustedAbiForSharedUser( setting.getPackageStates(), null /*scannedPackage*/)); - if (!useArtService() && // Skip for ART Service since it has its own dex file GC. - changedAbiCodePath != null && changedAbiCodePath.size() > 0) { - for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { - final String codePathString = changedAbiCodePath.get(i); - try { - mInstaller.rmdex(codePathString, - getDexCodeInstructionSet(getPreferredInstructionSet())); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } catch (InstallerException ignored) { - } - } - } // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same // SELinux domain. setting.fixSeInfoLocked(); @@ -4309,16 +4277,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService } }); - if (!useArtService()) { - // The background dexopt job is scheduled in DexOptHelper.initializeArtManagerLocal when - // ART Service is in use. - try { - mBackgroundDexOptService.systemReady(); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } - // Prune unused static shared libraries which have been cached a period of time schedulePruneUnusedStaticSharedLibraries(false /* delay */); @@ -6903,46 +6861,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } - /** @deprecated For legacy shell command only. */ - @Override - @Deprecated - public void legacyDumpProfiles(String packageName, boolean dumpClassesAndMethods) - throws LegacyDexoptDisabledException { - final Computer snapshot = snapshotComputer(); - AndroidPackage pkg = snapshot.getPackage(packageName); - if (pkg == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - - synchronized (mInstallLock) { - Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "dump profiles"); - mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods); - Trace.traceEnd(Trace.TRACE_TAG_DALVIK); - } - } - - /** @deprecated For legacy shell command only. */ - @Override - @Deprecated - public void legacyForceDexOpt(String packageName) throws LegacyDexoptDisabledException { - mDexOptHelper.forceDexOpt(snapshotComputer(), packageName); - } - - /** @deprecated For legacy shell command only. */ - @Override - @Deprecated - public void legacyReconcileSecondaryDexFiles(String packageName) - throws LegacyDexoptDisabledException { - final Computer snapshot = snapshotComputer(); - if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) { - return; - } else if (snapshot.isInstantAppInternal( - packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) { - return; - } - mDexManager.reconcileSecondaryDexFiles(packageName); - } - @Override @SuppressWarnings("GuardedBy") public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) { @@ -7512,33 +7430,20 @@ public class PackageManagerService implements PackageSender, TestUtilityService PackageManagerServiceUtils.enforceSystemOrRootOrShell( "Only the system or shell can delete oat artifacts"); - if (DexOptHelper.useArtService()) { - // TODO(chiuwinson): Retrieve filtered snapshot from Computer instance instead. - try (PackageManagerLocal.FilteredSnapshot filteredSnapshot = - PackageManagerServiceUtils.getPackageManagerLocal() - .withFilteredSnapshot()) { - try { - DeleteResult res = DexOptHelper.getArtManagerLocal().deleteDexoptArtifacts( - filteredSnapshot, packageName); - return res.getFreedBytes(); - } catch (IllegalArgumentException e) { - Log.e(TAG, e.toString()); - return -1; - } catch (IllegalStateException e) { - Slog.wtfStack(TAG, e.toString()); - return -1; - } - } - } else { - PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); - if (packageState == null || packageState.getPkg() == null) { - return -1; // error code of deleteOptimizedFiles - } + // TODO(chiuwinson): Retrieve filtered snapshot from Computer instance instead. + try (PackageManagerLocal.FilteredSnapshot filteredSnapshot = + PackageManagerServiceUtils.getPackageManagerLocal() + .withFilteredSnapshot()) { try { - return mDexManager.deleteOptimizedFiles( - ArtUtils.createArtPackageInfo(packageState.getPkg(), packageState)); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); + DeleteResult res = DexOptHelper.getArtManagerLocal().deleteDexoptArtifacts( + filteredSnapshot, packageName); + return res.getFreedBytes(); + } catch (IllegalArgumentException e) { + Log.e(TAG, e.toString()); + return -1; + } catch (IllegalStateException e) { + Slog.wtfStack(TAG, e.toString()); + return -1; } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java index 049737d42f51..83f3b16b31d1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.app.backup.IBackupManager; import android.content.ComponentName; @@ -138,8 +137,6 @@ public class PackageManagerServiceInjector { private final Singleton<DomainVerificationManagerInternal> mDomainVerificationManagerInternalProducer; private final Singleton<Handler> mHandlerProducer; - private final Singleton<BackgroundDexOptService> - mBackgroundDexOptService; // TODO(b/260124949): Remove this. private final Singleton<IBackupManager> mIBackupManager; private final Singleton<SharedLibrariesImpl> mSharedLibrariesProducer; private final Singleton<CrossProfileIntentFilterHelper> mCrossProfileIntentFilterHelperProducer; @@ -180,7 +177,6 @@ public class PackageManagerServiceInjector { SystemWrapper systemWrapper, ServiceProducer getLocalServiceProducer, ServiceProducer getSystemServiceProducer, - Producer<BackgroundDexOptService> backgroundDexOptService, Producer<IBackupManager> iBackupManager, Producer<SharedLibrariesImpl> sharedLibrariesProducer, Producer<CrossProfileIntentFilterHelper> crossProfileIntentFilterHelperProducer, @@ -234,7 +230,6 @@ public class PackageManagerServiceInjector { new Singleton<>( domainVerificationManagerInternalProducer); mHandlerProducer = new Singleton<>(handlerProducer); - mBackgroundDexOptService = new Singleton<>(backgroundDexOptService); mIBackupManager = new Singleton<>(iBackupManager); mSharedLibrariesProducer = new Singleton<>(sharedLibrariesProducer); mCrossProfileIntentFilterHelperProducer = new Singleton<>( @@ -409,11 +404,6 @@ public class PackageManagerServiceInjector { return getLocalService(ActivityManagerInternal.class); } - @Nullable - public BackgroundDexOptService getBackgroundDexOptService() { - return mBackgroundDexOptService.get(this, mPackageManager); - } - public IBackupManager getIBackupManager() { return mIBackupManager.get(this, mPackageManager); } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 2d797187b7f1..289373ee1456 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java @@ -105,7 +105,6 @@ public final class PackageManagerServiceTestParams { public boolean isEngBuild; public boolean isUserDebugBuild; public int sdkInt = Build.VERSION.SDK_INT; - public @Nullable BackgroundDexOptService backgroundDexOptService; public final String incrementalVersion = Build.VERSION.INCREMENTAL; public BroadcastHelper broadcastHelper; public AppDataHelper appDataHelper; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 4fb9b56a5f5f..a9e1725ea9a0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -29,7 +29,6 @@ import static android.content.pm.PackageManager.RESTRICTION_NONE; import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; import static com.android.server.pm.PackageManagerService.DEFAULT_FILE_ACCESS_MODE; -import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.accounts.IAccountManager; import android.annotation.NonNull; @@ -67,7 +66,6 @@ import android.content.pm.SharedLibraryInfo; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; -import android.content.pm.dex.ArtManager; import android.content.pm.dex.DexMetadataHelper; import android.content.pm.dex.ISnapshotRuntimeProfileCallback; import android.content.pm.parsing.ApkLite; @@ -102,8 +100,6 @@ import android.os.UserManager; import android.os.incremental.V4Signature; import android.os.storage.StorageManager; import android.permission.PermissionManager; -import android.system.ErrnoException; -import android.system.Os; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArrayMap; @@ -123,25 +119,20 @@ import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.art.ArtManagerLocal; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata; import com.android.server.pm.permission.LegacyPermissionManagerInternal; import com.android.server.pm.permission.PermissionAllowlist; import com.android.server.pm.verify.domain.DomainVerificationShell; -import dalvik.system.DexFile; - import libcore.io.IoUtils; import libcore.io.Streams; import libcore.util.HexEncoding; import java.io.BufferedReader; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStream; import java.io.PrintWriter; import java.net.URISyntaxException; import java.security.SecureRandom; @@ -154,7 +145,6 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CompletableFuture; @@ -400,15 +390,7 @@ class PackageManagerShellCommand extends ShellCommand { return runGetDomainVerificationAgent(); default: { if (ART_SERVICE_COMMANDS.contains(cmd)) { - if (DexOptHelper.useArtService()) { - return runArtServiceCommand(); - } else { - try { - return runLegacyDexoptCommand(cmd); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } + return runArtServiceCommand(); } Boolean domainVerificationResult = @@ -438,40 +420,6 @@ class PackageManagerShellCommand extends ShellCommand { return -1; } - private int runLegacyDexoptCommand(@NonNull String cmd) - throws RemoteException, LegacyDexoptDisabledException { - Installer.checkLegacyDexoptDisabled(); - - if (!PackageManagerServiceUtils.isRootOrShell(Binder.getCallingUid())) { - throw new SecurityException("Dexopt shell commands need root or shell access"); - } - - switch (cmd) { - case "compile": - return runCompile(); - case "reconcile-secondary-dex-files": - return runreconcileSecondaryDexFiles(); - case "force-dex-opt": - return runForceDexOpt(); - case "bg-dexopt-job": - return runBgDexOpt(); - case "cancel-bg-dexopt-job": - return cancelBgDexOptJob(); - case "delete-dexopt": - return runDeleteDexOpt(); - case "dump-profiles": - return runDumpProfiles(); - case "snapshot-profile": - return runSnapshotProfile(); - case "art": - getOutPrintWriter().println("ART Service not enabled"); - return -1; - default: - // Can't happen. - throw new IllegalArgumentException(); - } - } - /** * Shows module info * @@ -2067,340 +2015,6 @@ class PackageManagerShellCommand extends ShellCommand { } } - private int runCompile() throws RemoteException { - final PrintWriter pw = getOutPrintWriter(); - boolean forceCompilation = false; - boolean allPackages = false; - boolean clearProfileData = false; - String compilerFilter = null; - String compilationReason = null; - boolean secondaryDex = false; - String split = null; - - String opt; - while ((opt = getNextOption()) != null) { - switch (opt) { - case "-a": - allPackages = true; - break; - case "-c": - clearProfileData = true; - break; - case "-f": - forceCompilation = true; - break; - case "-m": - compilerFilter = getNextArgRequired(); - break; - case "-r": - compilationReason = getNextArgRequired(); - break; - case "--check-prof": - getNextArgRequired(); - pw.println("Warning: Ignoring obsolete flag --check-prof " - + "- it is unconditionally enabled now"); - break; - case "--reset": - forceCompilation = true; - clearProfileData = true; - compilationReason = "install"; - break; - case "--secondary-dex": - secondaryDex = true; - break; - case "--split": - split = getNextArgRequired(); - break; - default: - pw.println("Error: Unknown option: " + opt); - return 1; - } - } - - final boolean compilerFilterGiven = compilerFilter != null; - final boolean compilationReasonGiven = compilationReason != null; - // Make sure exactly one of -m, or -r is given. - if (compilerFilterGiven && compilationReasonGiven) { - pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " - + "at the same time"); - return 1; - } - if (!compilerFilterGiven && !compilationReasonGiven) { - pw.println("Cannot run without any of compilation filter (\"-m\") and compilation " - + "reason (\"-r\")"); - return 1; - } - - if (allPackages && split != null) { - pw.println("-a cannot be specified together with --split"); - return 1; - } - - if (secondaryDex && split != null) { - pw.println("--secondary-dex cannot be specified together with --split"); - return 1; - } - - String targetCompilerFilter = null; - if (compilerFilterGiven) { - if (!DexFile.isValidCompilerFilter(compilerFilter)) { - pw.println("Error: \"" + compilerFilter + - "\" is not a valid compilation filter."); - return 1; - } - targetCompilerFilter = compilerFilter; - } - if (compilationReasonGiven) { - int reason = -1; - for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) { - if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals( - compilationReason)) { - reason = i; - break; - } - } - if (reason == -1) { - pw.println("Error: Unknown compilation reason: " + compilationReason); - return 1; - } - targetCompilerFilter = - PackageManagerServiceCompilerMapping.getCompilerFilterForReason(reason); - } - - - List<String> packageNames = null; - if (allPackages) { - packageNames = mInterface.getAllPackages(); - // Compiling the system server is only supported from odrefresh, so skip it. - packageNames.removeIf(packageName -> PLATFORM_PACKAGE_NAME.equals(packageName)); - } else { - String packageName = getNextArg(); - if (packageName == null) { - pw.println("Error: package name not specified"); - return 1; - } - packageNames = Collections.singletonList(packageName); - } - - List<String> failedPackages = new ArrayList<>(); - int index = 0; - for (String packageName : packageNames) { - if (clearProfileData) { - mInterface.clearApplicationProfileData(packageName); - } - - if (allPackages) { - pw.println(++index + "/" + packageNames.size() + ": " + packageName); - pw.flush(); - } - - final boolean result = secondaryDex - ? mInterface.performDexOptSecondary( - packageName, targetCompilerFilter, forceCompilation) - : mInterface.performDexOptMode(packageName, true /* checkProfiles */, - targetCompilerFilter, forceCompilation, true /* bootComplete */, split); - if (!result) { - failedPackages.add(packageName); - } - } - - if (failedPackages.isEmpty()) { - pw.println("Success"); - return 0; - } else if (failedPackages.size() == 1) { - pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled"); - return 1; - } else { - pw.print("Failure: the following packages could not be compiled: "); - boolean is_first = true; - for (String packageName : failedPackages) { - if (is_first) { - is_first = false; - } else { - pw.print(", "); - } - pw.print(packageName); - } - pw.println(); - return 1; - } - } - - private int runreconcileSecondaryDexFiles() - throws RemoteException, LegacyDexoptDisabledException { - String packageName = getNextArg(); - mPm.legacyReconcileSecondaryDexFiles(packageName); - return 0; - } - - public int runForceDexOpt() throws RemoteException, LegacyDexoptDisabledException { - mPm.legacyForceDexOpt(getNextArgRequired()); - return 0; - } - - private int runBgDexOpt() throws RemoteException, LegacyDexoptDisabledException { - String opt = getNextOption(); - - if (opt == null) { - List<String> packageNames = new ArrayList<>(); - String arg; - while ((arg = getNextArg()) != null) { - packageNames.add(arg); - } - if (!BackgroundDexOptService.getService().runBackgroundDexoptJob( - packageNames.isEmpty() ? null : packageNames)) { - getOutPrintWriter().println("Failure"); - return -1; - } - } else { - String extraArg = getNextArg(); - if (extraArg != null) { - getErrPrintWriter().println("Invalid argument: " + extraArg); - return -1; - } - - switch (opt) { - case "--cancel": - return cancelBgDexOptJob(); - - case "--disable": - BackgroundDexOptService.getService().setDisableJobSchedulerJobs(true); - break; - - case "--enable": - BackgroundDexOptService.getService().setDisableJobSchedulerJobs(false); - break; - - default: - getErrPrintWriter().println("Unknown option: " + opt); - return -1; - } - } - - getOutPrintWriter().println("Success"); - return 0; - } - - private int cancelBgDexOptJob() throws RemoteException, LegacyDexoptDisabledException { - BackgroundDexOptService.getService().cancelBackgroundDexoptJob(); - getOutPrintWriter().println("Success"); - return 0; - } - - private int runDeleteDexOpt() throws RemoteException { - PrintWriter pw = getOutPrintWriter(); - String packageName = getNextArg(); - if (TextUtils.isEmpty(packageName)) { - pw.println("Error: no package name"); - return 1; - } - long freedBytes = mPm.deleteOatArtifactsOfPackage(packageName); - if (freedBytes < 0) { - pw.println("Error: delete failed"); - return 1; - } - pw.println("Success: freed " + freedBytes + " bytes"); - Slog.i(TAG, "delete-dexopt " + packageName + " ,freed " + freedBytes + " bytes"); - return 0; - } - - private int runDumpProfiles() throws RemoteException, LegacyDexoptDisabledException { - final PrintWriter pw = getOutPrintWriter(); - boolean dumpClassesAndMethods = false; - - String opt; - while ((opt = getNextOption()) != null) { - switch (opt) { - case "--dump-classes-and-methods": - dumpClassesAndMethods = true; - break; - default: - pw.println("Error: Unknown option: " + opt); - return 1; - } - } - - String packageName = getNextArg(); - mPm.legacyDumpProfiles(packageName, dumpClassesAndMethods); - return 0; - } - - private int runSnapshotProfile() throws RemoteException { - PrintWriter pw = getOutPrintWriter(); - - // Parse the arguments - final String packageName = getNextArg(); - final boolean isBootImage = "android".equals(packageName); - - String codePath = null; - String opt; - while ((opt = getNextArg()) != null) { - switch (opt) { - case "--code-path": - if (isBootImage) { - pw.write("--code-path cannot be used for the boot image."); - return -1; - } - codePath = getNextArg(); - break; - default: - pw.write("Unknown arg: " + opt); - return -1; - } - } - - // If no code path was explicitly requested, select the base code path. - String baseCodePath = null; - if (!isBootImage) { - PackageInfo packageInfo = mInterface.getPackageInfo(packageName, /* flags */ 0, - /* userId */0); - if (packageInfo == null) { - pw.write("Package not found " + packageName); - return -1; - } - baseCodePath = packageInfo.applicationInfo.getBaseCodePath(); - if (codePath == null) { - codePath = baseCodePath; - } - } - - // Create the profile snapshot. - final SnapshotRuntimeProfileCallback callback = new SnapshotRuntimeProfileCallback(); - // The calling package is needed to debug permission access. - final String callingPackage = (Binder.getCallingUid() == Process.ROOT_UID) - ? "root" : "com.android.shell"; - final int profileType = isBootImage - ? ArtManager.PROFILE_BOOT_IMAGE : ArtManager.PROFILE_APPS; - if (!mInterface.getArtManager().isRuntimeProfilingEnabled(profileType, callingPackage)) { - pw.println("Error: Runtime profiling is not enabled"); - return -1; - } - mInterface.getArtManager().snapshotRuntimeProfile(profileType, packageName, - codePath, callback, callingPackage); - if (!callback.waitTillDone()) { - pw.println("Error: callback not called"); - return callback.mErrCode; - } - - // Copy the snapshot profile to the output profile file. - try (InputStream inStream = new AutoCloseInputStream(callback.mProfileReadFd)) { - final String outputFileSuffix = isBootImage || Objects.equals(baseCodePath, codePath) - ? "" : ("-" + new File(codePath).getName()); - final String outputProfilePath = - ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + packageName + outputFileSuffix + ".prof"; - try (OutputStream outStream = new FileOutputStream(outputProfilePath)) { - Streams.copy(inStream, outStream); - } - // Give read permissions to the other group. - Os.chmod(outputProfilePath, /*mode*/ DEFAULT_FILE_ACCESS_MODE); - } catch (IOException | ErrnoException e) { - pw.println("Error when reading the profile fd: " + e.getMessage()); - e.printStackTrace(pw); - return -1; - } - return 0; - } - private ArrayList<String> getRemainingArgs() { ArrayList<String> args = new ArrayList<>(); String arg; @@ -5212,11 +4826,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" get-domain-verification-agent"); pw.println(" Displays the component name of the domain verification agent on device."); pw.println(""); - if (DexOptHelper.useArtService()) { - printArtServiceHelp(); - } else { - printLegacyDexoptHelp(); - } + printArtServiceHelp(); pw.println(""); mDomainVerificationShell.printHelp(pw); pw.println(""); @@ -5235,75 +4845,6 @@ class PackageManagerShellCommand extends ShellCommand { ipw.decreaseIndent(); } - private void printLegacyDexoptHelp() { - final PrintWriter pw = getOutPrintWriter(); - pw.println(" compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]"); - pw.println(" [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)"); - pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\". Options are:"); - pw.println(" -a: compile all packages"); - pw.println(" -c: clear profile data before compiling"); - pw.println(" -f: force compilation even if not needed"); - pw.println(" -m: select compilation mode"); - pw.println(" MODE is one of the dex2oat compiler filters:"); - pw.println(" verify"); - pw.println(" speed-profile"); - pw.println(" speed"); - pw.println(" -r: select compilation reason"); - pw.println(" REASON is one of:"); - for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) { - pw.println(" " + PackageManagerServiceCompilerMapping.REASON_STRINGS[i]); - } - pw.println(" --reset: restore package to its post-install state"); - pw.println(" --check-prof (true | false): ignored - this is always true"); - pw.println(" --secondary-dex: compile app secondary dex files"); - pw.println(" --split SPLIT: compile only the given split name"); - pw.println(""); - pw.println(" force-dex-opt PACKAGE"); - pw.println(" Force immediate execution of dex opt for the given PACKAGE."); - pw.println(""); - pw.println(" delete-dexopt PACKAGE"); - pw.println(" Delete dex optimization results for the given PACKAGE."); - pw.println(""); - pw.println(" bg-dexopt-job [PACKAGE... | --cancel | --disable | --enable]"); - pw.println(" Controls the background job that optimizes dex files:"); - pw.println(" Without flags, run background optimization immediately on the given"); - pw.println(" PACKAGEs, or all packages if none is specified, and wait until the job"); - pw.println(" finishes. Note that the command only runs the background optimizer logic."); - pw.println(" It will run even if the device is not in the idle maintenance mode. If a"); - pw.println(" job is already running (including one started automatically by the"); - pw.println(" system) it will wait for it to finish before starting. A background job"); - pw.println(" will not be started automatically while one started this way is running."); - pw.println(" --cancel: Cancels any currently running background optimization job"); - pw.println(" immediately. This cancels jobs started either automatically by the"); - pw.println(" system or through this command. Note that cancelling a currently"); - pw.println(" running bg-dexopt-job command requires running this command from a"); - pw.println(" separate adb shell."); - pw.println(" --disable: Disables background jobs from being started by the job"); - pw.println(" scheduler. Does not affect bg-dexopt-job invocations from the shell."); - pw.println(" Does not imply --cancel. This state will be lost when the"); - pw.println(" system_server process exits."); - pw.println(" --enable: Enables background jobs to be started by the job scheduler"); - pw.println(" again, if previously disabled by --disable."); - pw.println(" cancel-bg-dexopt-job"); - pw.println(" Same as bg-dexopt-job --cancel."); - pw.println(""); - pw.println(" reconcile-secondary-dex-files TARGET-PACKAGE"); - pw.println(" Reconciles the package secondary dex files with the generated oat files."); - pw.println(""); - pw.println(" dump-profiles [--dump-classes-and-methods] TARGET-PACKAGE"); - pw.println(" Dumps method/class profile files to"); - pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION - + "TARGET-PACKAGE-primary.prof.txt."); - pw.println(" --dump-classes-and-methods: passed along to the profman binary to"); - pw.println(" switch to the format used by 'profman --create-profile-from'."); - pw.println(""); - pw.println(" snapshot-profile TARGET-PACKAGE [--code-path path]"); - pw.println(" Take a snapshot of the package profiles to"); - pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION - + "TARGET-PACKAGE[-code-path].prof"); - pw.println(" If TARGET-PACKAGE=android it will take a snapshot of the boot image"); - } - private static class LocalIntentReceiver { private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>(); diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index 70352be01096..6b1278177b85 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -23,7 +23,6 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_CE; import static android.os.storage.StorageManager.FLAG_STORAGE_DE; import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; -import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX; @@ -49,7 +48,6 @@ import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.util.ArrayUtils; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; @@ -511,32 +509,9 @@ final class RemovePackageHelper { } removeCodePathLI(codeFile); - removeDexFilesLI(allCodePaths, instructionSets); - } - @GuardedBy("mPm.mInstallLock") - private void removeDexFilesLI(@NonNull List<String> allCodePaths, - @Nullable String[] instructionSets) { - if (!allCodePaths.isEmpty()) { - if (instructionSets == null) { - throw new IllegalStateException("instructionSet == null"); - } - // TODO(b/265813358): ART Service currently doesn't support deleting optimized artifacts - // relative to an arbitrary APK path. Skip this and rely on its file GC instead. - if (!DexOptHelper.useArtService()) { - String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); - for (String codePath : allCodePaths) { - for (String dexCodeInstructionSet : dexCodeInstructionSets) { - try { - mPm.mInstaller.rmdex(codePath, dexCodeInstructionSet); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } catch (Installer.InstallerException ignored) { - } - } - } - } - } + // TODO(b/265813358): ART Service currently doesn't support deleting optimized artifacts + // relative to an arbitrary APK path. Skip this and rely on its file GC instead. } void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) { diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index ae47aa823245..e49dc8250bc7 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -18,7 +18,6 @@ package com.android.server.pm.dex; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -28,7 +27,6 @@ import android.content.pm.PackageManager; import android.content.pm.dex.ArtManager; import android.content.pm.dex.ArtManager.ProfileType; import android.content.pm.dex.ArtManagerInternal; -import android.content.pm.dex.DexMetadataHelper; import android.content.pm.dex.ISnapshotRuntimeProfileCallback; import android.content.pm.dex.PackageOptimizationInfo; import android.os.Binder; @@ -39,8 +37,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; -import android.os.UserHandle; -import android.system.Os; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; @@ -53,22 +49,17 @@ import com.android.server.LocalServices; import com.android.server.art.ArtManagerLocal; import com.android.server.pm.DexOptHelper; import com.android.server.pm.Installer; -import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.PackageManagerLocal; import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageManagerServiceCompilerMapping; import com.android.server.pm.PackageManagerServiceUtils; -import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.PackageStateInternal; import dalvik.system.DexFile; import dalvik.system.VMRuntime; import libcore.io.IoUtils; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; @@ -259,91 +250,27 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } // All good, create the profile snapshot. - if (DexOptHelper.useArtService()) { - ParcelFileDescriptor fd; - - try (PackageManagerLocal.FilteredSnapshot snapshot = - PackageManagerServiceUtils.getPackageManagerLocal() - .withFilteredSnapshot()) { - fd = DexOptHelper.getArtManagerLocal().snapshotAppProfile( - snapshot, packageName, splitName); - } catch (IllegalArgumentException e) { - // ArtManagerLocal.snapshotAppProfile couldn't find the package or split. Since - // we've checked them above this can only happen due to race, i.e. the package got - // removed. So let's report it as SNAPSHOT_FAILED_PACKAGE_NOT_FOUND even if it was - // for the split. - // TODO(mast): Reuse the same snapshot to avoid this race. - postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND); - return; - } catch (IllegalStateException | ArtManagerLocal.SnapshotProfileException e) { - postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); - return; - } - - postSuccess(packageName, fd, callback); - } else { - int appId = UserHandle.getAppId(info.applicationInfo.uid); - if (appId < 0) { - postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); - Slog.wtf(TAG, "AppId is -1 for package: " + packageName); - return; - } - - try { - createProfileSnapshot(packageName, ArtManager.getProfileName(splitName), codePath, - appId, callback); - // Destroy the snapshot, we no longer need it. - destroyProfileSnapshot(packageName, ArtManager.getProfileName(splitName)); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } - } - } - - private void createProfileSnapshot(String packageName, String profileName, String classpath, - int appId, ISnapshotRuntimeProfileCallback callback) - throws LegacyDexoptDisabledException { - // Ask the installer to snapshot the profile. - try { - if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) { - postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); - return; - } - } catch (InstallerException e) { - postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); + ParcelFileDescriptor fd; + + try (PackageManagerLocal.FilteredSnapshot snapshot = + PackageManagerServiceUtils.getPackageManagerLocal() + .withFilteredSnapshot()) { + fd = DexOptHelper.getArtManagerLocal().snapshotAppProfile( + snapshot, packageName, splitName); + } catch (IllegalArgumentException e) { + // ArtManagerLocal.snapshotAppProfile couldn't find the package or split. Since + // we've checked them above this can only happen due to race, i.e. the package got + // removed. So let's report it as SNAPSHOT_FAILED_PACKAGE_NOT_FOUND even if it was + // for the split. + // TODO(mast): Reuse the same snapshot to avoid this race. + postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND); return; - } - - // Open the snapshot and invoke the callback. - File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName); - - ParcelFileDescriptor fd = null; - try { - fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY); - if (fd == null || !fd.getFileDescriptor().valid()) { - postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); - } else { - postSuccess(packageName, fd, callback); - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" - + snapshotProfile, e); + } catch (IllegalStateException | ArtManagerLocal.SnapshotProfileException e) { postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); - } - } - - private void destroyProfileSnapshot(String packageName, String profileName) - throws LegacyDexoptDisabledException { - if (DEBUG) { - Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName); + return; } - try { - mInstaller.destroyProfileSnapshot(packageName, profileName); - } catch (InstallerException e) { - Slog.e(TAG, "Failed to destroy profile snapshot for " + packageName + ":" + profileName, - e); - } + postSuccess(packageName, fd, callback); } @Override @@ -368,42 +295,19 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) { - if (DexOptHelper.useArtService()) { - ParcelFileDescriptor fd; - - try (PackageManagerLocal.FilteredSnapshot snapshot = - PackageManagerServiceUtils.getPackageManagerLocal() - .withFilteredSnapshot()) { - fd = DexOptHelper.getArtManagerLocal().snapshotBootImageProfile(snapshot); - } catch (IllegalStateException | ArtManagerLocal.SnapshotProfileException e) { - postError(callback, BOOT_IMAGE_ANDROID_PACKAGE, - ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); - return; - } - - postSuccess(BOOT_IMAGE_ANDROID_PACKAGE, fd, callback); - } else { - // Combine the profiles for boot classpath and system server classpath. - // This avoids having yet another type of profiles and simplifies the processing. - String classpath = String.join( - ":", Os.getenv("BOOTCLASSPATH"), Os.getenv("SYSTEMSERVERCLASSPATH")); - - final String standaloneSystemServerJars = Os.getenv("STANDALONE_SYSTEMSERVER_JARS"); - if (standaloneSystemServerJars != null) { - classpath = String.join(":", classpath, standaloneSystemServerJars); - } - - try { - // Create the snapshot. - createProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME, - classpath, - /*appId*/ -1, callback); - // Destroy the snapshot, we no longer need it. - destroyProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME); - } catch (LegacyDexoptDisabledException e) { - throw new RuntimeException(e); - } + ParcelFileDescriptor fd; + + try (PackageManagerLocal.FilteredSnapshot snapshot = + PackageManagerServiceUtils.getPackageManagerLocal() + .withFilteredSnapshot()) { + fd = DexOptHelper.getArtManagerLocal().snapshotBootImageProfile(snapshot); + } catch (IllegalStateException | ArtManagerLocal.SnapshotProfileException e) { + postError(callback, BOOT_IMAGE_ANDROID_PACKAGE, + ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); + return; } + + postSuccess(BOOT_IMAGE_ANDROID_PACKAGE, fd, callback); } /** @@ -451,117 +355,6 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { }); } - /** - * Prepare the application profiles. - * For all code paths: - * - create the current primary profile to save time at app startup time. - * - copy the profiles from the associated dex metadata file to the reference profile. - */ - public void prepareAppProfiles(AndroidPackage pkg, @UserIdInt int user, - boolean updateReferenceProfileContent) throws LegacyDexoptDisabledException { - final int appId = UserHandle.getAppId(pkg.getUid()); - if (user < 0) { - Slog.wtf(TAG, "Invalid user id: " + user); - return; - } - if (appId < 0) { - Slog.wtf(TAG, "Invalid app id: " + appId); - return; - } - try { - ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg); - for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) { - String codePath = codePathsProfileNames.keyAt(i); - String profileName = codePathsProfileNames.valueAt(i); - String dexMetadataPath = null; - // Passing the dex metadata file to the prepare method will update the reference - // profile content. As such, we look for the dex metadata file only if we need to - // perform an update. - if (updateReferenceProfileContent) { - File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath)); - dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath(); - } - synchronized (mInstaller) { - boolean result = mInstaller.prepareAppProfile(pkg.getPackageName(), user, appId, - profileName, codePath, dexMetadataPath); - if (!result) { - Slog.e(TAG, "Failed to prepare profile for " + - pkg.getPackageName() + ":" + codePath); - } - } - } - } catch (InstallerException e) { - Slog.e(TAG, "Failed to prepare profile for " + pkg.getPackageName(), e); - } - } - - /** - * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}. - */ - public void prepareAppProfiles(AndroidPackage pkg, int[] user, - boolean updateReferenceProfileContent) throws LegacyDexoptDisabledException { - for (int i = 0; i < user.length; i++) { - prepareAppProfiles(pkg, user[i], updateReferenceProfileContent); - } - } - - /** - * Clear the profiles for the given package. - */ - public void clearAppProfiles(AndroidPackage pkg) throws LegacyDexoptDisabledException { - try { - ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg); - for (int i = packageProfileNames.size() - 1; i >= 0; i--) { - String profileName = packageProfileNames.valueAt(i); - mInstaller.clearAppProfiles(pkg.getPackageName(), profileName); - } - } catch (InstallerException e) { - Slog.w(TAG, String.valueOf(e)); - } - } - - /** - * Dumps the profiles for the given package. - */ - public void dumpProfiles(AndroidPackage pkg, boolean dumpClassesAndMethods) - throws LegacyDexoptDisabledException { - final int sharedGid = UserHandle.getSharedAppGid(pkg.getUid()); - try { - ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg); - for (int i = packageProfileNames.size() - 1; i >= 0; i--) { - String codePath = packageProfileNames.keyAt(i); - String profileName = packageProfileNames.valueAt(i); - mInstaller.dumpProfiles(sharedGid, pkg.getPackageName(), profileName, codePath, - dumpClassesAndMethods); - } - } catch (InstallerException e) { - Slog.w(TAG, "Failed to dump profiles", e); - } - } - - /** - * Build the profiles names for all the package code paths (excluding resource only paths). - * Return the map [code path -> profile name]. - */ - private ArrayMap<String, String> getPackageProfileNames(AndroidPackage pkg) { - ArrayMap<String, String> result = new ArrayMap<>(); - if (pkg.isDeclaredHavingCode()) { - result.put(pkg.getBaseApkPath(), ArtManager.getProfileName(null)); - } - - String[] splitCodePaths = pkg.getSplitCodePaths(); - int[] splitFlags = pkg.getSplitFlags(); - String[] splitNames = pkg.getSplitNames(); - if (!ArrayUtils.isEmpty(splitCodePaths)) { - for (int i = 0; i < splitCodePaths.length; i++) { - if ((splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) { - result.put(splitCodePaths[i], ArtManager.getProfileName(splitNames[i])); - } - } - } - return result; - } - // Constants used for logging compilation filter to TRON. // DO NOT CHANGE existing values. // @@ -792,6 +585,7 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { String packageName, String activityName, long version) { // For example: /data/misc/iorapd/com.google.android.GoogleCamera/ // 60092239/com.android.camera.CameraLauncher/compiled_traces/compiled_trace.pb + // TODO(b/258223472): Clean up iorap code. Path tracePath = Paths.get(IORAP_DIR, packageName, Long.toString(version), diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java index 57f4a5ddb2bd..a24a2318d423 100644 --- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java +++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java @@ -22,13 +22,11 @@ import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATI import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK; import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK; -import android.app.job.JobParameters; import android.os.SystemClock; import android.util.Slog; import android.util.jar.StrictJarFile; import com.android.internal.art.ArtStatsLog; -import com.android.server.pm.BackgroundDexOptService; import com.android.server.pm.PackageManagerService; import java.io.IOException; @@ -303,42 +301,4 @@ public class ArtStatsLogUtils { ArtStatsLog.ART_DATUM_REPORTED__UFFD_SUPPORT__ART_UFFD_SUPPORT_UNKNOWN); } } - - private static final Map<Integer, Integer> STATUS_MAP = - Map.of(BackgroundDexOptService.STATUS_UNSPECIFIED, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_UNKNOWN, - BackgroundDexOptService.STATUS_OK, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_JOB_FINISHED, - BackgroundDexOptService.STATUS_ABORT_BY_CANCELLATION, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_BY_CANCELLATION, - BackgroundDexOptService.STATUS_ABORT_NO_SPACE_LEFT, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_NO_SPACE_LEFT, - BackgroundDexOptService.STATUS_ABORT_THERMAL, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_THERMAL, - BackgroundDexOptService.STATUS_ABORT_BATTERY, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_BATTERY, - BackgroundDexOptService.STATUS_DEX_OPT_FAILED, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_JOB_FINISHED, - BackgroundDexOptService.STATUS_FATAL_ERROR, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_FATAL_ERROR); - - /** Helper class to write background dexopt job stats to statsd. */ - public static class BackgroundDexoptJobStatsLogger { - /** Writes background dexopt job stats to statsd. */ - public void write(@BackgroundDexOptService.Status int status, - @JobParameters.StopReason int cancellationReason, - long durationMs) { - ArtStatsLog.write( - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED, - STATUS_MAP.getOrDefault(status, - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_UNKNOWN), - cancellationReason, - durationMs, - 0, // deprecated, used to be durationIncludingSleepMs - 0, // optimizedPackagesCount - 0, // packagesDependingOnBootClasspathCount - 0, // totalPackagesCount - ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_UNKNOWN); - } - } } diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 78c13f854fe4..e93d3206a4f1 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -17,7 +17,6 @@ package com.android.server.pm.dex; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; -import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; import static java.util.function.Function.identity; @@ -31,12 +30,9 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackagePartitions; import android.os.BatteryManager; -import android.os.FileUtils; import android.os.PowerManager; -import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; -import android.os.storage.StorageManager; import android.util.Log; import android.util.Slog; import android.util.jar.StrictJarFile; @@ -44,8 +40,6 @@ import android.util.jar.StrictJarFile; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.Installer; -import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.PackageDexOptimizer; import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageManagerServiceUtils; @@ -54,8 +48,6 @@ import dalvik.system.VMRuntime; import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -496,60 +488,6 @@ public class DexManager { } /** - * Perform dexopt on with the given {@code options} on the secondary dex files. - * @return true if all secondary dex files were processed successfully (compiled or skipped - * because they don't need to be compiled).. - */ - public boolean dexoptSecondaryDex(DexoptOptions options) throws LegacyDexoptDisabledException { - if (isPlatformPackage(options.getPackageName())) { - // We could easily redirect to #dexoptSystemServer in this case. But there should be - // no-one calling this method directly for system server. - // As such we prefer to abort in this case. - Slog.wtf(TAG, "System server jars should be optimized with dexoptSystemServer"); - return false; - } - - PackageDexOptimizer pdo = getPackageDexOptimizer(options); - String packageName = options.getPackageName(); - PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName); - if (useInfo.getDexUseInfoMap().isEmpty()) { - if (DEBUG) { - Slog.d(TAG, "No secondary dex use for package:" + packageName); - } - // Nothing to compile, return true. - return true; - } - boolean success = true; - for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) { - String dexPath = entry.getKey(); - DexUseInfo dexUseInfo = entry.getValue(); - - PackageInfo pkg; - try { - pkg = getPackageManager().getPackageInfo(packageName, /*flags*/0, - dexUseInfo.getOwnerUserId()); - } catch (RemoteException e) { - throw new AssertionError(e); - } - // It may be that the package gets uninstalled while we try to compile its - // secondary dex files. If that's the case, just ignore. - // Note that we don't break the entire loop because the package might still be - // installed for other users. - if (pkg == null) { - Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName - + " for user " + dexUseInfo.getOwnerUserId()); - mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId()); - continue; - } - - int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath, - dexUseInfo, options); - success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED); - } - return success; - } - - /** * Select the dex optimizer based on the force parameter. * Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust * the necessary dexopt flags to make sure that compilation is not skipped. This avoid @@ -564,101 +502,6 @@ public class DexManager { } /** - * Reconcile the information we have about the secondary dex files belonging to - * {@code packagName} and the actual dex files. For all dex files that were - * deleted, update the internal records and delete any generated oat files. - */ - public void reconcileSecondaryDexFiles(String packageName) - throws LegacyDexoptDisabledException { - PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName); - if (useInfo.getDexUseInfoMap().isEmpty()) { - if (DEBUG) { - Slog.d(TAG, "No secondary dex use for package:" + packageName); - } - // Nothing to reconcile. - return; - } - - boolean updated = false; - for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) { - String dexPath = entry.getKey(); - DexUseInfo dexUseInfo = entry.getValue(); - PackageInfo pkg = null; - try { - // Note that we look for the package in the PackageManager just to be able - // to get back the real app uid and its storage kind. These are only used - // to perform extra validation in installd. - // TODO(calin): maybe a bit overkill. - pkg = getPackageManager().getPackageInfo(packageName, /*flags*/0, - dexUseInfo.getOwnerUserId()); - } catch (RemoteException ignore) { - // Can't happen, DexManager is local. - } - if (pkg == null) { - // It may be that the package was uninstalled while we process the secondary - // dex files. - Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName - + " for user " + dexUseInfo.getOwnerUserId()); - // Update the usage and continue, another user might still have the package. - updated = mPackageDexUsage.removeUserPackage( - packageName, dexUseInfo.getOwnerUserId()) || updated; - continue; - } - - // Special handle system server files. - // We don't need an installd call because we have permissions to check if the file - // exists. - if (isPlatformPackage(packageName)) { - if (!Files.exists(Paths.get(dexPath))) { - if (DEBUG) { - Slog.w(TAG, "A dex file previously loaded by System Server does not exist " - + " anymore: " + dexPath); - } - updated = mPackageDexUsage.removeUserPackage( - packageName, dexUseInfo.getOwnerUserId()) || updated; - } - continue; - } - - // This is a regular application. - ApplicationInfo info = pkg.applicationInfo; - int flags = 0; - if (info.deviceProtectedDataDir != null && - FileUtils.contains(info.deviceProtectedDataDir, dexPath)) { - flags |= StorageManager.FLAG_STORAGE_DE; - } else if (info.credentialProtectedDataDir!= null && - FileUtils.contains(info.credentialProtectedDataDir, dexPath)) { - flags |= StorageManager.FLAG_STORAGE_CE; - } else { - Slog.e(TAG, "Could not infer CE/DE storage for path " + dexPath); - updated = mPackageDexUsage.removeDexFile( - packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated; - continue; - } - - boolean dexStillExists = true; - synchronized(mInstallLock) { - try { - String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]); - dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName, - info.uid, isas, info.volumeUuid, flags); - } catch (InstallerException e) { - Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath + - " : " + e.getMessage()); - } - } - if (!dexStillExists) { - updated = mPackageDexUsage.removeDexFile( - packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated; - } - - } - if (updated) { - mPackageDexUsage.maybeWriteAsync(); - } - } - - /** * Return all packages that contain records of secondary dex files. */ public Set<String> getAllPackagesWithSecondaryDexFiles() { @@ -852,33 +695,6 @@ public class DexManager { return isBtmCritical; } - /** - * Deletes all the optimizations files generated by ART. - * This is best effort, and the method will log but not throw errors - * for individual deletes - * - * @param packageInfo the package information. - * @return the number of freed bytes or -1 if there was an error in the process. - */ - public long deleteOptimizedFiles(ArtPackageInfo packageInfo) - throws LegacyDexoptDisabledException { - long freedBytes = 0; - boolean hadErrors = false; - final String packageName = packageInfo.getPackageName(); - for (String codePath : packageInfo.getCodePaths()) { - for (String isa : packageInfo.getInstructionSets()) { - try { - freedBytes += mInstaller.deleteOdex(packageName, codePath, isa, - packageInfo.getOatDir()); - } catch (InstallerException e) { - Log.e(TAG, "Failed deleting oat files for " + codePath, e); - hadErrors = true; - } - } - } - return hadErrors ? -1 : freedBytes; - } - public static class RegisterDexModuleResult { public RegisterDexModuleResult() { this(false, null); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java deleted file mode 100644 index 9a7ee4d7887b..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java +++ /dev/null @@ -1,684 +0,0 @@ -/* - * Copyright (C) 2021 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.pm; - -import static com.android.server.pm.BackgroundDexOptService.STATUS_DEX_OPT_FAILED; -import static com.android.server.pm.BackgroundDexOptService.STATUS_FATAL_ERROR; -import static com.android.server.pm.BackgroundDexOptService.STATUS_OK; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertThrows; - -import android.annotation.Nullable; -import android.app.job.JobInfo; -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.HandlerThread; -import android.os.PowerManager; -import android.os.Process; -import android.os.SystemProperties; -import android.util.Log; - -import com.android.internal.util.IndentingPrintWriter; -import com.android.server.LocalServices; -import com.android.server.PinnerService; -import com.android.server.pm.dex.DexManager; -import com.android.server.pm.dex.DexoptOptions; - -import org.junit.After; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import java.io.ByteArrayOutputStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.stream.Collectors; - -@RunWith(MockitoJUnitRunner.class) -public final class BackgroundDexOptServiceUnitTest { - private static final String TAG = BackgroundDexOptServiceUnitTest.class.getSimpleName(); - - private static final long USABLE_SPACE_NORMAL = 1_000_000_000; - private static final long STORAGE_LOW_BYTES = 1_000_000; - - private static final long TEST_WAIT_TIMEOUT_MS = 10_000; - - private static final String PACKAGE_AAA = "aaa"; - private static final List<String> DEFAULT_PACKAGE_LIST = List.of(PACKAGE_AAA, "bbb"); - private int mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_PERFORMED; - - // Store expected dexopt sequence for verification. - private ArrayList<DexOptInfo> mDexInfoSequence = new ArrayList<>(); - - @Mock - private Context mContext; - @Mock - private PackageManagerService mPackageManager; - @Mock - private DexOptHelper mDexOptHelper; - @Mock - private DexManager mDexManager; - @Mock - private PinnerService mPinnerService; - @Mock - private JobScheduler mJobScheduler; - @Mock - private BackgroundDexOptService.Injector mInjector; - @Mock - private BackgroundDexOptJobService mJobServiceForPostBoot; - @Mock - private BackgroundDexOptJobService mJobServiceForIdle; - - private final JobParameters mJobParametersForPostBoot = - createJobParameters(BackgroundDexOptService.JOB_POST_BOOT_UPDATE); - private final JobParameters mJobParametersForIdle = - createJobParameters(BackgroundDexOptService.JOB_IDLE_OPTIMIZE); - - private static JobParameters createJobParameters(int jobId) { - JobParameters params = mock(JobParameters.class); - when(params.getJobId()).thenReturn(jobId); - return params; - } - - private BackgroundDexOptService mService; - - private StartAndWaitThread mDexOptThread; - private StartAndWaitThread mCancelThread; - - @Before - public void setUp() throws Exception { - // These tests are only applicable to the legacy BackgroundDexOptService and cannot be run - // when ART Service is enabled. - Assume.assumeFalse(SystemProperties.getBoolean("dalvik.vm.useartservice", false)); - - when(mInjector.getCallingUid()).thenReturn(Process.FIRST_APPLICATION_UID); - when(mInjector.getContext()).thenReturn(mContext); - when(mInjector.getDexOptHelper()).thenReturn(mDexOptHelper); - when(mInjector.getDexManager()).thenReturn(mDexManager); - when(mInjector.getPinnerService()).thenReturn(mPinnerService); - when(mInjector.getJobScheduler()).thenReturn(mJobScheduler); - when(mInjector.getPackageManagerService()).thenReturn(mPackageManager); - - // These mocking can be overwritten in some tests but still keep it here as alternative - // takes too many repetitive codes. - when(mInjector.getDataDirUsableSpace()).thenReturn(USABLE_SPACE_NORMAL); - when(mInjector.getDataDirStorageLowBytes()).thenReturn(STORAGE_LOW_BYTES); - when(mInjector.getDexOptThermalCutoff()).thenReturn(PowerManager.THERMAL_STATUS_CRITICAL); - when(mInjector.getCurrentThermalStatus()).thenReturn(PowerManager.THERMAL_STATUS_NONE); - when(mInjector.supportSecondaryDex()).thenReturn(true); - setupDexOptHelper(); - - mService = new BackgroundDexOptService(mInjector); - } - - private void setupDexOptHelper() { - when(mDexOptHelper.getOptimizablePackages(any())).thenReturn(DEFAULT_PACKAGE_LIST); - when(mDexOptHelper.performDexOptWithStatus(any())).thenAnswer(inv -> { - DexoptOptions opt = inv.getArgument(0); - if (opt.getPackageName().equals(PACKAGE_AAA)) { - return mDexOptResultForPackageAAA; - } - return PackageDexOptimizer.DEX_OPT_PERFORMED; - }); - when(mDexOptHelper.performDexOpt(any())).thenReturn(true); - } - - @After - public void tearDown() throws Exception { - LocalServices.removeServiceForTest(BackgroundDexOptService.class); - } - - @Test - public void testGetService() { - assertThat(BackgroundDexOptService.getService()).isEqualTo(mService); - } - - @Test - public void testBootCompleted() throws Exception { - initUntilBootCompleted(); - } - - @Test - public void testNoExecutionForIdleJobBeforePostBootUpdate() throws Exception { - initUntilBootCompleted(); - - assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isFalse(); - } - - @Test - public void testNoExecutionForLowStorage() throws Exception { - initUntilBootCompleted(); - when(mPackageManager.isStorageLow()).thenReturn(true); - - assertThat(mService.onStartJob(mJobServiceForPostBoot, - mJobParametersForPostBoot)).isFalse(); - verify(mDexOptHelper, never()).performDexOpt(any()); - } - - @Test - public void testNoExecutionForNoOptimizablePackages() throws Exception { - initUntilBootCompleted(); - when(mDexOptHelper.getOptimizablePackages(any())).thenReturn(Collections.emptyList()); - - assertThat(mService.onStartJob(mJobServiceForPostBoot, - mJobParametersForPostBoot)).isFalse(); - verify(mDexOptHelper, never()).performDexOpt(any()); - } - - @Test - public void testPostBootUpdateFullRun() throws Exception { - initUntilBootCompleted(); - - runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK, - /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ null); - } - - @Test - public void testPostBootUpdateFullRunWithPackageFailure() throws Exception { - mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_FAILED; - - initUntilBootCompleted(); - - runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_DEX_OPT_FAILED, - /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ PACKAGE_AAA); - - assertThat(getFailedPackageNamesPrimary()).containsExactly(PACKAGE_AAA); - assertThat(getFailedPackageNamesSecondary()).isEmpty(); - } - - @Test - public void testIdleJobFullRun() throws Exception { - initUntilBootCompleted(); - runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK, - /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ null); - runFullJob(mJobServiceForIdle, mJobParametersForIdle, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK, - /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ null); - } - - @Test - public void testIdleJobFullRunWithFailureOnceAndSuccessAfterUpdate() throws Exception { - mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_FAILED; - - initUntilBootCompleted(); - - runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_DEX_OPT_FAILED, - /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ PACKAGE_AAA); - - assertThat(getFailedPackageNamesPrimary()).containsExactly(PACKAGE_AAA); - assertThat(getFailedPackageNamesSecondary()).isEmpty(); - - runFullJob(mJobServiceForIdle, mJobParametersForIdle, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK, - /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ PACKAGE_AAA); - - assertThat(getFailedPackageNamesPrimary()).containsExactly(PACKAGE_AAA); - assertThat(getFailedPackageNamesSecondary()).isEmpty(); - - mService.notifyPackageChanged(PACKAGE_AAA); - - assertThat(getFailedPackageNamesPrimary()).isEmpty(); - assertThat(getFailedPackageNamesSecondary()).isEmpty(); - - // Succeed this time. - mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_PERFORMED; - - runFullJob(mJobServiceForIdle, mJobParametersForIdle, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK, - /* totalJobFinishedWithParams= */ 2, /* expectedSkippedPackage= */ null); - - assertThat(getFailedPackageNamesPrimary()).isEmpty(); - assertThat(getFailedPackageNamesSecondary()).isEmpty(); - } - - @Test - public void testIdleJobFullRunWithFatalError() throws Exception { - initUntilBootCompleted(); - runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK, - /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ null); - - doThrow(RuntimeException.class).when(mDexOptHelper).performDexOptWithStatus(any()); - - runFullJob(mJobServiceForIdle, mJobParametersForIdle, - /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_FATAL_ERROR, - /* totalJobFinishedWithParams= */ 1, /* expectedSkippedPackage= */ null); - } - - @Test - public void testSystemReadyWhenDisabled() throws Exception { - when(mInjector.isBackgroundDexOptDisabled()).thenReturn(true); - - mService.systemReady(); - - verify(mContext, never()).registerReceiver(any(), any()); - } - - @Test - public void testStopByCancelFlag() throws Exception { - when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread()); - initUntilBootCompleted(); - - assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - - ArgumentCaptor<Runnable> argDexOptThreadRunnable = ArgumentCaptor.forClass(Runnable.class); - verify(mInjector, atLeastOnce()).createAndStartThread(any(), - argDexOptThreadRunnable.capture()); - - // Stopping requires a separate thread - HandlerThread cancelThread = new HandlerThread("Stopping"); - cancelThread.start(); - when(mInjector.createAndStartThread(any(), any())).thenReturn(cancelThread); - - // Cancel - assertThat(mService.onStopJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - - // Capture Runnable for cancel - ArgumentCaptor<Runnable> argCancelThreadRunnable = ArgumentCaptor.forClass(Runnable.class); - verify(mInjector, atLeastOnce()).createAndStartThread(any(), - argCancelThreadRunnable.capture()); - - // Execute cancelling part - cancelThread.getThreadHandler().post(argCancelThreadRunnable.getValue()); - - verify(mDexOptHelper, timeout(TEST_WAIT_TIMEOUT_MS)).controlDexOptBlocking(true); - - // Dexopt thread run and cancelled - argDexOptThreadRunnable.getValue().run(); - - // Wait until cancellation Runnable is completed. - assertThat(cancelThread.getThreadHandler().runWithScissors( - argCancelThreadRunnable.getValue(), TEST_WAIT_TIMEOUT_MS)).isTrue(); - - // Now cancel completed - verify(mJobServiceForPostBoot).jobFinished(mJobParametersForPostBoot, true); - verifyLastControlDexOptBlockingCall(false); - } - - @Test - public void testPostUpdateCancelFirst() throws Exception { - initUntilBootCompleted(); - when(mInjector.createAndStartThread(any(), any())).thenAnswer( - i -> createAndStartExecutionThread(i.getArgument(0), i.getArgument(1))); - - // Start - assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - // Cancel - assertThat(mService.onStopJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - - mCancelThread.runActualRunnable(); - - // Wait until cancel has set the flag. - verify(mDexOptHelper, timeout(TEST_WAIT_TIMEOUT_MS)).controlDexOptBlocking( - true); - - mDexOptThread.runActualRunnable(); - - // All threads should finish. - mDexOptThread.join(TEST_WAIT_TIMEOUT_MS); - mCancelThread.join(TEST_WAIT_TIMEOUT_MS); - - // Retry later if post boot job was cancelled - verify(mJobServiceForPostBoot).jobFinished(mJobParametersForPostBoot, true); - verifyLastControlDexOptBlockingCall(false); - } - - @Test - public void testPostUpdateCancelLater() throws Exception { - initUntilBootCompleted(); - when(mInjector.createAndStartThread(any(), any())).thenAnswer( - i -> createAndStartExecutionThread(i.getArgument(0), i.getArgument(1))); - - // Start - assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - // Cancel - assertThat(mService.onStopJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - - // Dexopt thread runs and finishes - mDexOptThread.runActualRunnable(); - mDexOptThread.join(TEST_WAIT_TIMEOUT_MS); - - mCancelThread.runActualRunnable(); - mCancelThread.join(TEST_WAIT_TIMEOUT_MS); - - // Already completed before cancel, so no rescheduling. - verify(mJobServiceForPostBoot).jobFinished(mJobParametersForPostBoot, false); - verify(mDexOptHelper, never()).controlDexOptBlocking(true); - } - - @Test - public void testPeriodicJobCancelFirst() throws Exception { - initUntilBootCompleted(); - when(mInjector.createAndStartThread(any(), any())).thenAnswer( - i -> createAndStartExecutionThread(i.getArgument(0), i.getArgument(1))); - - // Start and finish post boot job - assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - mDexOptThread.runActualRunnable(); - mDexOptThread.join(TEST_WAIT_TIMEOUT_MS); - - // Start - assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isTrue(); - // Cancel - assertThat(mService.onStopJob(mJobServiceForIdle, mJobParametersForIdle)).isTrue(); - - mCancelThread.runActualRunnable(); - - // Wait until cancel has set the flag. - verify(mDexOptHelper, timeout(TEST_WAIT_TIMEOUT_MS)).controlDexOptBlocking( - true); - - mDexOptThread.runActualRunnable(); - - // All threads should finish. - mDexOptThread.join(TEST_WAIT_TIMEOUT_MS); - mCancelThread.join(TEST_WAIT_TIMEOUT_MS); - - // The job should be rescheduled. - verify(mJobServiceForIdle).jobFinished(mJobParametersForIdle, true /* wantsReschedule */); - verifyLastControlDexOptBlockingCall(false); - } - - @Test - public void testPeriodicJobCancelLater() throws Exception { - initUntilBootCompleted(); - when(mInjector.createAndStartThread(any(), any())).thenAnswer( - i -> createAndStartExecutionThread(i.getArgument(0), i.getArgument(1))); - - // Start and finish post boot job - assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - mDexOptThread.runActualRunnable(); - mDexOptThread.join(TEST_WAIT_TIMEOUT_MS); - - // Start - assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isTrue(); - // Cancel - assertThat(mService.onStopJob(mJobServiceForIdle, mJobParametersForIdle)).isTrue(); - - // Dexopt thread finishes first. - mDexOptThread.runActualRunnable(); - mDexOptThread.join(TEST_WAIT_TIMEOUT_MS); - - mCancelThread.runActualRunnable(); - mCancelThread.join(TEST_WAIT_TIMEOUT_MS); - - // Always reschedule for periodic job - verify(mJobServiceForIdle).jobFinished(mJobParametersForIdle, false); - verify(mDexOptHelper, never()).controlDexOptBlocking(true); - } - - @Test - public void testStopByThermal() throws Exception { - when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread()); - initUntilBootCompleted(); - - assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue(); - - ArgumentCaptor<Runnable> argThreadRunnable = ArgumentCaptor.forClass(Runnable.class); - verify(mInjector, atLeastOnce()).createAndStartThread(any(), argThreadRunnable.capture()); - - // Thermal cancel level - when(mInjector.getCurrentThermalStatus()).thenReturn(PowerManager.THERMAL_STATUS_CRITICAL); - - argThreadRunnable.getValue().run(); - - verify(mJobServiceForPostBoot).jobFinished(mJobParametersForPostBoot, true); - verifyLastControlDexOptBlockingCall(false); - } - - @Test - public void testRunShellCommandWithInvalidUid() { - // Test uid cannot execute the command APIs - assertThrows(SecurityException.class, () -> mService.runBackgroundDexoptJob(null)); - } - - @Test - public void testCancelShellCommandWithInvalidUid() { - // Test uid cannot execute the command APIs - assertThrows(SecurityException.class, () -> mService.cancelBackgroundDexoptJob()); - } - - @Test - public void testDisableJobSchedulerJobs() throws Exception { - when(mInjector.getCallingUid()).thenReturn(Process.SHELL_UID); - mService.setDisableJobSchedulerJobs(true); - assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isFalse(); - verify(mDexOptHelper, never()).performDexOpt(any()); - verify(mDexOptHelper, never()).performDexOptWithStatus(any()); - } - - @Test - public void testSetDisableJobSchedulerJobsWithInvalidUid() { - // Test uid cannot execute the command APIs - assertThrows(SecurityException.class, () -> mService.setDisableJobSchedulerJobs(true)); - } - - private void initUntilBootCompleted() throws Exception { - ArgumentCaptor<BroadcastReceiver> argReceiver = ArgumentCaptor.forClass( - BroadcastReceiver.class); - ArgumentCaptor<IntentFilter> argIntentFilter = ArgumentCaptor.forClass(IntentFilter.class); - - mService.systemReady(); - - verify(mContext).registerReceiver(argReceiver.capture(), argIntentFilter.capture()); - assertThat(argIntentFilter.getValue().getAction(0)).isEqualTo(Intent.ACTION_BOOT_COMPLETED); - - argReceiver.getValue().onReceive(mContext, null); - - verify(mContext).unregisterReceiver(argReceiver.getValue()); - ArgumentCaptor<JobInfo> argJobs = ArgumentCaptor.forClass(JobInfo.class); - verify(mJobScheduler, times(2)).schedule(argJobs.capture()); - - List<Integer> expectedJobIds = Arrays.asList(BackgroundDexOptService.JOB_IDLE_OPTIMIZE, - BackgroundDexOptService.JOB_POST_BOOT_UPDATE); - List<Integer> jobIds = argJobs.getAllValues().stream().map(job -> job.getId()).collect( - Collectors.toList()); - assertThat(jobIds).containsExactlyElementsIn(expectedJobIds); - } - - private void verifyLastControlDexOptBlockingCall(boolean expected) throws Exception { - ArgumentCaptor<Boolean> argDexOptBlock = ArgumentCaptor.forClass(Boolean.class); - verify(mDexOptHelper, atLeastOnce()).controlDexOptBlocking(argDexOptBlock.capture()); - assertThat(argDexOptBlock.getValue()).isEqualTo(expected); - } - - private void runFullJob(BackgroundDexOptJobService jobService, JobParameters params, - boolean expectedReschedule, int expectedStatus, int totalJobFinishedWithParams, - @Nullable String expectedSkippedPackage) throws Exception { - when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread()); - addFullRunSequence(expectedSkippedPackage); - assertThat(mService.onStartJob(jobService, params)).isTrue(); - - ArgumentCaptor<Runnable> argThreadRunnable = ArgumentCaptor.forClass(Runnable.class); - verify(mInjector, atLeastOnce()).createAndStartThread(any(), argThreadRunnable.capture()); - - try { - argThreadRunnable.getValue().run(); - } catch (RuntimeException e) { - if (expectedStatus != STATUS_FATAL_ERROR) { - throw e; - } - } - - verify(jobService, times(totalJobFinishedWithParams)).jobFinished(params, - expectedReschedule); - // Never block - verify(mDexOptHelper, never()).controlDexOptBlocking(true); - if (expectedStatus != STATUS_FATAL_ERROR) { - verifyPerformDexOpt(); - } - assertThat(getLastExecutionStatus()).isEqualTo(expectedStatus); - } - - private void verifyPerformDexOpt() { - InOrder inOrder = inOrder(mDexOptHelper); - inOrder.verify(mDexOptHelper).getOptimizablePackages(any()); - for (DexOptInfo info : mDexInfoSequence) { - if (info.isPrimary) { - verify(mDexOptHelper).performDexOptWithStatus( - argThat((option) -> option.getPackageName().equals(info.packageName) - && !option.isDexoptOnlySecondaryDex())); - } else { - inOrder.verify(mDexOptHelper).performDexOpt( - argThat((option) -> option.getPackageName().equals(info.packageName) - && option.isDexoptOnlySecondaryDex())); - } - } - - // Even InOrder cannot check the order if the same call is made multiple times. - // To check the order across multiple runs, we reset the mock so that order can be checked - // in each call. - mDexInfoSequence.clear(); - reset(mDexOptHelper); - setupDexOptHelper(); - } - - private String findDumpValueForKey(String key) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - PrintWriter pw = new PrintWriter(out, true); - IndentingPrintWriter writer = new IndentingPrintWriter(pw, ""); - try { - mService.dump(writer); - writer.flush(); - Log.i(TAG, "dump output:" + out.toString()); - for (String line : out.toString().split(System.lineSeparator())) { - String[] vals = line.split(":"); - if (vals[0].equals(key)) { - if (vals.length == 2) { - return vals[1].strip(); - } else { - break; - } - } - } - return ""; - } finally { - writer.close(); - } - } - - List<String> findStringListFromDump(String key) { - String values = findDumpValueForKey(key); - if (values.isEmpty()) { - return Collections.emptyList(); - } - return Arrays.asList(values.split(",")); - } - - private List<String> getFailedPackageNamesPrimary() { - return findStringListFromDump("mFailedPackageNamesPrimary"); - } - - private List<String> getFailedPackageNamesSecondary() { - return findStringListFromDump("mFailedPackageNamesSecondary"); - } - - private int getLastExecutionStatus() { - return Integer.parseInt(findDumpValueForKey("mLastExecutionStatus")); - } - - private static class DexOptInfo { - public final String packageName; - public final boolean isPrimary; - - private DexOptInfo(String packageName, boolean isPrimary) { - this.packageName = packageName; - this.isPrimary = isPrimary; - } - } - - private void addFullRunSequence(@Nullable String expectedSkippedPackage) { - for (String packageName : DEFAULT_PACKAGE_LIST) { - if (packageName.equals(expectedSkippedPackage)) { - // only fails primary dexopt in mocking but add secodary - mDexInfoSequence.add(new DexOptInfo(packageName, /* isPrimary= */ false)); - } else { - mDexInfoSequence.add(new DexOptInfo(packageName, /* isPrimary= */ true)); - mDexInfoSequence.add(new DexOptInfo(packageName, /* isPrimary= */ false)); - } - } - } - - private static class StartAndWaitThread extends Thread { - private final Runnable mActualRunnable; - private final CountDownLatch mLatch = new CountDownLatch(1); - - private StartAndWaitThread(String name, Runnable runnable) { - super(name); - mActualRunnable = runnable; - } - - private void runActualRunnable() { - mLatch.countDown(); - } - - @Override - public void run() { - // Thread is started but does not run actual code. This is for controlling the execution - // order while still meeting Thread.isAlive() check. - try { - mLatch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - mActualRunnable.run(); - } - } - - private Thread createAndStartExecutionThread(String name, Runnable runnable) { - final boolean isDexOptThread = !name.equals("DexOptCancel"); - StartAndWaitThread thread = new StartAndWaitThread(name, runnable); - if (isDexOptThread) { - mDexOptThread = thread; - } else { - mCancelThread = thread; - } - thread.start(); - return thread; - } -} diff --git a/test-base/Android.bp b/test-base/Android.bp index 70a95400bd9e..d65a4e44440e 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -14,37 +14,22 @@ // limitations under the License. // +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + // Build the android.test.base library // =================================== // This contains the junit.framework and android.test classes that were in // Android API level 25 excluding those from android.test.runner. // Also contains the com.android.internal.util.Predicate[s] classes. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - // SPDX-license-identifier-CPL-1.0 - default_applicable_licenses: ["frameworks_base_test-base_license"], -} - -license { - name: "frameworks_base_test-base_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - "SPDX-license-identifier-CPL-1.0", - ], - license_text: [ - "src/junit/cpl-v10.html", - ], -} - java_sdk_library { name: "android.test.base", - srcs: [":android-test-base-sources"], + srcs: [ + ":android-test-base-sources", + ":frameworks-base-test-junit-framework", + ], errorprone: { javacflags: ["-Xep:DepAnn:ERROR"], @@ -84,7 +69,10 @@ java_library_static { ], installable: false, - srcs: [":android-test-base-sources"], + srcs: [ + ":android-test-base-sources", + ":frameworks-base-test-junit-framework", + ], errorprone: { javacflags: ["-Xep:DepAnn:ERROR"], @@ -104,8 +92,7 @@ java_library_static { name: "android.test.base-minus-junit", srcs: [ - "src/android/**/*.java", - "src/com/**/*.java", + "src/**/*.java", ], sdk_version: "current", diff --git a/test-base/hiddenapi/Android.bp b/test-base/hiddenapi/Android.bp index 1466590ef311..4c59b10ba423 100644 --- a/test-base/hiddenapi/Android.bp +++ b/test-base/hiddenapi/Android.bp @@ -15,12 +15,7 @@ // package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], + default_applicable_licenses: ["Android-Apache-2.0"], } // Provided solely to contribute information about which hidden parts of the android.test.base diff --git a/test-junit/Android.bp b/test-junit/Android.bp new file mode 100644 index 000000000000..8d3d439e034e --- /dev/null +++ b/test-junit/Android.bp @@ -0,0 +1,53 @@ +// +// Copyright (C) 2024 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 { + default_applicable_licenses: ["frameworks-base-test-junit-license"], +} + +license { + name: "frameworks-base-test-junit-license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-CPL-1.0", + ], + license_text: [ + "src/junit/cpl-v10.html", + ], +} + +filegroup { + name: "frameworks-base-test-junit-framework", + srcs: [ + "src/junit/framework/**/*.java", + ], + path: "src", + visibility: [ + "//frameworks/base/test-base", + ], +} + +filegroup { + name: "frameworks-base-test-junit-runner", + srcs: [ + "src/junit/runner/**/*.java", + "src/junit/textui/**/*.java", + ], + path: "src", + visibility: [ + "//frameworks/base/test-runner", + ], +} diff --git a/test-base/src/junit/MODULE_LICENSE_CPL b/test-junit/src/junit/MODULE_LICENSE_CPL index e69de29bb2d1..e69de29bb2d1 100644 --- a/test-base/src/junit/MODULE_LICENSE_CPL +++ b/test-junit/src/junit/MODULE_LICENSE_CPL diff --git a/test-base/src/junit/README.android b/test-junit/src/junit/README.android index 1384a1fedda2..1384a1fedda2 100644 --- a/test-base/src/junit/README.android +++ b/test-junit/src/junit/README.android diff --git a/test-base/src/junit/cpl-v10.html b/test-junit/src/junit/cpl-v10.html index 36aa208d4a29..36aa208d4a29 100644 --- a/test-base/src/junit/cpl-v10.html +++ b/test-junit/src/junit/cpl-v10.html diff --git a/test-base/src/junit/framework/Assert.java b/test-junit/src/junit/framework/Assert.java index 3dcc23d71c19..3dcc23d71c19 100644 --- a/test-base/src/junit/framework/Assert.java +++ b/test-junit/src/junit/framework/Assert.java diff --git a/test-base/src/junit/framework/AssertionFailedError.java b/test-junit/src/junit/framework/AssertionFailedError.java index 0d7802c431c6..0d7802c431c6 100644 --- a/test-base/src/junit/framework/AssertionFailedError.java +++ b/test-junit/src/junit/framework/AssertionFailedError.java diff --git a/test-base/src/junit/framework/ComparisonCompactor.java b/test-junit/src/junit/framework/ComparisonCompactor.java index e540f03b87d3..e540f03b87d3 100644 --- a/test-base/src/junit/framework/ComparisonCompactor.java +++ b/test-junit/src/junit/framework/ComparisonCompactor.java diff --git a/test-base/src/junit/framework/ComparisonFailure.java b/test-junit/src/junit/framework/ComparisonFailure.java index 507799328a44..507799328a44 100644 --- a/test-base/src/junit/framework/ComparisonFailure.java +++ b/test-junit/src/junit/framework/ComparisonFailure.java diff --git a/test-base/src/junit/framework/Protectable.java b/test-junit/src/junit/framework/Protectable.java index e1432370cfaf..e1432370cfaf 100644 --- a/test-base/src/junit/framework/Protectable.java +++ b/test-junit/src/junit/framework/Protectable.java diff --git a/test-base/src/junit/framework/Test.java b/test-junit/src/junit/framework/Test.java index a016ee8308f1..a016ee8308f1 100644 --- a/test-base/src/junit/framework/Test.java +++ b/test-junit/src/junit/framework/Test.java diff --git a/test-base/src/junit/framework/TestCase.java b/test-junit/src/junit/framework/TestCase.java index b047ec9e1afc..b047ec9e1afc 100644 --- a/test-base/src/junit/framework/TestCase.java +++ b/test-junit/src/junit/framework/TestCase.java diff --git a/test-base/src/junit/framework/TestFailure.java b/test-junit/src/junit/framework/TestFailure.java index 6662b1fab1b2..6662b1fab1b2 100644 --- a/test-base/src/junit/framework/TestFailure.java +++ b/test-junit/src/junit/framework/TestFailure.java diff --git a/test-base/src/junit/framework/TestListener.java b/test-junit/src/junit/framework/TestListener.java index 9b6944361b9d..9b6944361b9d 100644 --- a/test-base/src/junit/framework/TestListener.java +++ b/test-junit/src/junit/framework/TestListener.java diff --git a/test-base/src/junit/framework/TestResult.java b/test-junit/src/junit/framework/TestResult.java index 3052e94074fd..3052e94074fd 100644 --- a/test-base/src/junit/framework/TestResult.java +++ b/test-junit/src/junit/framework/TestResult.java diff --git a/test-base/src/junit/framework/TestSuite.java b/test-junit/src/junit/framework/TestSuite.java index 336efd1800d7..336efd1800d7 100644 --- a/test-base/src/junit/framework/TestSuite.java +++ b/test-junit/src/junit/framework/TestSuite.java diff --git a/test-runner/src/junit/runner/BaseTestRunner.java b/test-junit/src/junit/runner/BaseTestRunner.java index b2fa16c91da2..b2fa16c91da2 100644 --- a/test-runner/src/junit/runner/BaseTestRunner.java +++ b/test-junit/src/junit/runner/BaseTestRunner.java diff --git a/test-runner/src/junit/runner/StandardTestSuiteLoader.java b/test-junit/src/junit/runner/StandardTestSuiteLoader.java index 808963a5aea0..808963a5aea0 100644 --- a/test-runner/src/junit/runner/StandardTestSuiteLoader.java +++ b/test-junit/src/junit/runner/StandardTestSuiteLoader.java diff --git a/test-runner/src/junit/runner/TestRunListener.java b/test-junit/src/junit/runner/TestRunListener.java index 0e9581989eee..0e9581989eee 100644 --- a/test-runner/src/junit/runner/TestRunListener.java +++ b/test-junit/src/junit/runner/TestRunListener.java diff --git a/test-runner/src/junit/runner/TestSuiteLoader.java b/test-junit/src/junit/runner/TestSuiteLoader.java index 9cc6d81e125e..9cc6d81e125e 100644 --- a/test-runner/src/junit/runner/TestSuiteLoader.java +++ b/test-junit/src/junit/runner/TestSuiteLoader.java diff --git a/test-runner/src/junit/runner/Version.java b/test-junit/src/junit/runner/Version.java index dd88c03372c8..dd88c03372c8 100644 --- a/test-runner/src/junit/runner/Version.java +++ b/test-junit/src/junit/runner/Version.java diff --git a/test-runner/src/junit/runner/package-info.java b/test-junit/src/junit/runner/package-info.java index 364e3621456e..364e3621456e 100644 --- a/test-runner/src/junit/runner/package-info.java +++ b/test-junit/src/junit/runner/package-info.java diff --git a/test-runner/src/junit/textui/ResultPrinter.java b/test-junit/src/junit/textui/ResultPrinter.java index b4914529bf4f..b4914529bf4f 100644 --- a/test-runner/src/junit/textui/ResultPrinter.java +++ b/test-junit/src/junit/textui/ResultPrinter.java diff --git a/test-runner/src/junit/textui/TestRunner.java b/test-junit/src/junit/textui/TestRunner.java index 046448e5e76a..046448e5e76a 100644 --- a/test-runner/src/junit/textui/TestRunner.java +++ b/test-junit/src/junit/textui/TestRunner.java diff --git a/test-runner/src/junit/textui/package-info.java b/test-junit/src/junit/textui/package-info.java index 28b2ef46b582..28b2ef46b582 100644 --- a/test-runner/src/junit/textui/package-info.java +++ b/test-junit/src/junit/textui/package-info.java diff --git a/test-mock/Android.bp b/test-mock/Android.bp index f37d2d17973e..e29d321e5105 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -17,12 +17,7 @@ // Build the android.test.mock library // =================================== package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], + default_applicable_licenses: ["Android-Apache-2.0"], } java_sdk_library { diff --git a/test-runner/Android.bp b/test-runner/Android.bp index 21e09d3221ce..6b5be3cba204 100644 --- a/test-runner/Android.bp +++ b/test-runner/Android.bp @@ -14,29 +14,19 @@ // limitations under the License. // -// Build the android.test.runner library -// ===================================== package { - // See: http://go/android-license-faq - default_applicable_licenses: ["frameworks_base_test-runner_license"], -} - -license { - name: "frameworks_base_test-runner_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - "SPDX-license-identifier-CPL-1.0", - ], - license_text: [ - "src/junit/cpl-v10.html", - ], + default_applicable_licenses: ["Android-Apache-2.0"], } +// Build the android.test.runner library +// ===================================== java_sdk_library { name: "android.test.runner", - srcs: [":android-test-runner-sources"], + srcs: [ + ":android-test-runner-sources", + ":frameworks-base-test-junit-runner", + ], errorprone: { javacflags: ["-Xep:DepAnn:ERROR"], diff --git a/test-runner/src/junit/MODULE_LICENSE_CPL b/test-runner/src/junit/MODULE_LICENSE_CPL deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/test-runner/src/junit/MODULE_LICENSE_CPL +++ /dev/null diff --git a/test-runner/src/junit/README.android b/test-runner/src/junit/README.android deleted file mode 100644 index 1384a1fedda2..000000000000 --- a/test-runner/src/junit/README.android +++ /dev/null @@ -1,11 +0,0 @@ -URL: https://github.com/junit-team/junit4 -License: Common Public License Version 1.0 -License File: cpl-v10.html - -This is JUnit 4.10 source that was previously part of the Android Public API. -Where necessary it has been patched to be compatible (according to Android API -requirements) with JUnit 3.8. - -These are copied here to ensure that the android.test.runner target remains -compatible with the last version of the Android API (25) that contained these -classes even when external/junit is upgraded to a later version. diff --git a/test-runner/src/junit/cpl-v10.html b/test-runner/src/junit/cpl-v10.html deleted file mode 100644 index 36aa208d4a29..000000000000 --- a/test-runner/src/junit/cpl-v10.html +++ /dev/null @@ -1,125 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> -<HTML> -<HEAD> -<TITLE>Common Public License - v 1.0</TITLE> -<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> -</HEAD> - -<BODY BGCOLOR="#FFFFFF" VLINK="#800000"> - - -<P ALIGN="CENTER"><B>Common Public License - v 1.0</B> -<P><B></B><FONT SIZE="3"></FONT> -<P><FONT SIZE="3"></FONT><FONT SIZE="2">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.</FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"><B>1. DEFINITIONS</B></FONT> -<P><FONT SIZE="2">"Contribution" means:</FONT> - -<UL><FONT SIZE="2">a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and<BR CLEAR="LEFT"> -b) in the case of each subsequent Contributor:</FONT></UL> - - -<UL><FONT SIZE="2">i) changes to the Program, and</FONT></UL> - - -<UL><FONT SIZE="2">ii) additions to the Program;</FONT></UL> - - -<UL><FONT SIZE="2">where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. </FONT><FONT SIZE="2">A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. </FONT><FONT SIZE="2">Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. </FONT></UL> - -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2">"Contributor" means any person or entity that distributes the Program.</FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> -<P><FONT SIZE="2">"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. </FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2">"Program" means the Contributions distributed in accordance with this Agreement.</FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2">"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.</FONT> -<P><FONT SIZE="2"><B></B></FONT> -<P><FONT SIZE="2"><B>2. GRANT OF RIGHTS</B></FONT> - -<UL><FONT SIZE="2"></FONT><FONT SIZE="2">a) </FONT><FONT SIZE="2">Subject to the terms of this Agreement, each Contributor hereby grants</FONT><FONT SIZE="2"> Recipient a non-exclusive, worldwide, royalty-free copyright license to</FONT><FONT SIZE="2" COLOR="#FF0000"> </FONT><FONT SIZE="2">reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</FONT></UL> - - -<UL><FONT SIZE="2"></FONT></UL> - - -<UL><FONT SIZE="2"></FONT><FONT SIZE="2">b) Subject to the terms of this Agreement, each Contributor hereby grants </FONT><FONT SIZE="2">Recipient a non-exclusive, worldwide,</FONT><FONT SIZE="2" COLOR="#008000"> </FONT><FONT SIZE="2">royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. </FONT></UL> - - -<UL><FONT SIZE="2"></FONT></UL> - - -<UL><FONT SIZE="2">c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.</FONT></UL> - - -<UL><FONT SIZE="2"></FONT></UL> - - -<UL><FONT SIZE="2">d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. </FONT></UL> - - -<UL><FONT SIZE="2"></FONT></UL> - -<P><FONT SIZE="2"><B>3. REQUIREMENTS</B></FONT> -<P><FONT SIZE="2"><B></B>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</FONT> - -<UL><FONT SIZE="2">a) it complies with the terms and conditions of this Agreement; and</FONT></UL> - - -<UL><FONT SIZE="2">b) its license agreement:</FONT></UL> - - -<UL><FONT SIZE="2">i) effectively disclaims</FONT><FONT SIZE="2"> on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; </FONT></UL> - - -<UL><FONT SIZE="2">ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; </FONT></UL> - - -<UL><FONT SIZE="2">iii)</FONT><FONT SIZE="2"> states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</FONT></UL> - - -<UL><FONT SIZE="2">iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</FONT><FONT SIZE="2" COLOR="#0000FF"> </FONT><FONT SIZE="2" COLOR="#FF0000"></FONT></UL> - - -<UL><FONT SIZE="2" COLOR="#FF0000"></FONT><FONT SIZE="2"></FONT></UL> - -<P><FONT SIZE="2">When the Program is made available in source code form:</FONT> - -<UL><FONT SIZE="2">a) it must be made available under this Agreement; and </FONT></UL> - - -<UL><FONT SIZE="2">b) a copy of this Agreement must be included with each copy of the Program. </FONT></UL> - -<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT> -<P><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT><FONT SIZE="2">Contributors may not remove or alter any copyright notices contained within the Program. </FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2">Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. </FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"><B>4. COMMERCIAL DISTRIBUTION</B></FONT> -<P><FONT SIZE="2">Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.</FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2">For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"></FONT> -<P><FONT SIZE="2" COLOR="#0000FF"></FONT><FONT SIZE="2"><B>5. NO WARRANTY</B></FONT> -<P><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is</FONT><FONT SIZE="2"> solely responsible for determining the appropriateness of using and distributing </FONT><FONT SIZE="2">the Program</FONT><FONT SIZE="2"> and assumes all risks associated with its exercise of rights under this Agreement</FONT><FONT SIZE="2">, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, </FONT><FONT SIZE="2">programs or equipment, and unavailability or interruption of operations</FONT><FONT SIZE="2">. </FONT><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2"><B>6. DISCLAIMER OF LIABILITY</B></FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES </FONT><FONT SIZE="2">(INCLUDING WITHOUT LIMITATION LOST PROFITS),</FONT><FONT SIZE="2"> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"><B>7. GENERAL</B></FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2">If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2">If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. </FONT><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2">All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. </FONT><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2">Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to </FONT><FONT SIZE="2">publish new versions (including revisions) of this Agreement from time to </FONT><FONT SIZE="2">time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. </FONT><FONT SIZE="2">Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new </FONT><FONT SIZE="2">version. </FONT><FONT SIZE="2">Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, </FONT><FONT SIZE="2">by implication, estoppel or otherwise</FONT><FONT SIZE="2">.</FONT><FONT SIZE="2"> All rights in the Program not expressly granted under this Agreement are reserved.</FONT> -<P><FONT SIZE="2"></FONT> -<P><FONT SIZE="2">This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.</FONT> -<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> -<P><FONT SIZE="2"></FONT> - -</BODY> - -</HTML>
\ No newline at end of file diff --git a/test-runner/tests/Android.bp b/test-runner/tests/Android.bp index ac21bcb9d124..aad2bee8cb84 100644 --- a/test-runner/tests/Android.bp +++ b/test-runner/tests/Android.bp @@ -13,12 +13,7 @@ // limitations under the License. package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], + default_applicable_licenses: ["Android-Apache-2.0"], } android_test { |