Merge "cherrypick I4e1ca9fddc6374fd80108e4f9a3b9745f8598507 add new make target for offline docs with only the API reference. Also update the default index.html landing page to redirect to the reference." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 5ac2e77..9d4674e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8142,6 +8142,7 @@
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
@@ -9560,6 +9561,7 @@
method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+ method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
}
@@ -20180,37 +20182,129 @@
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+ field public static final java.lang.String TAG_ARTIST = "Artist";
+ field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+ field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+ field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+ field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+ field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+ field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+ field public static final java.lang.String TAG_COMPRESSION = "Compression";
+ field public static final java.lang.String TAG_CONTRAST = "Contrast";
+ field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+ field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
field public static final java.lang.String TAG_DATETIME = "DateTime";
field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+ field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+ field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+ field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
field public static final java.lang.String TAG_FLASH = "Flash";
+ field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+ field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+ field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+ field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+ field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+ field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+ field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+ field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+ field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+ field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+ field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+ field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+ field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+ field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+ field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+ field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+ field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+ field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+ field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+ field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+ field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
field public static final java.lang.String TAG_MAKE = "Make";
+ field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+ field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
field public static final java.lang.String TAG_MODEL = "Model";
+ field public static final java.lang.String TAG_OECF = "OECF";
field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+ field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+ field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+ field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+ field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+ field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+ field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+ field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+ field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+ field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+ field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+ field public static final java.lang.String TAG_SATURATION = "Saturation";
+ field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+ field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+ field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+ field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+ field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+ field public static final java.lang.String TAG_SOFTWARE = "Software";
+ field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+ field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+ field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+ field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+ field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+ field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+ field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+ field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+ field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+ field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+ field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+ field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+ field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+ field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+ field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+ field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
field public static final int WHITEBALANCE_AUTO = 0; // 0x0
field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
}
@@ -29361,6 +29455,147 @@
}
+package android.os.health {
+
+ public class HealthStats {
+ method public java.lang.String getDataType();
+ method public long getMeasurement(int);
+ method public int getMeasurementKeyAt(int);
+ method public int getMeasurementKeyCount();
+ method public java.util.Map<java.lang.String, java.lang.Long> getMeasurements(int);
+ method public int getMeasurementsKeyAt(int);
+ method public int getMeasurementsKeyCount();
+ method public java.util.Map<java.lang.String, android.os.health.HealthStats> getStats(int);
+ method public int getStatsKeyAt(int);
+ method public int getStatsKeyCount();
+ method public android.os.health.TimerStat getTimer(int);
+ method public int getTimerCount(int);
+ method public int getTimerKeyAt(int);
+ method public int getTimerKeyCount();
+ method public long getTimerTime(int);
+ method public java.util.Map<java.lang.String, android.os.health.TimerStat> getTimers(int);
+ method public int getTimersKeyAt(int);
+ method public int getTimersKeyCount();
+ method public boolean hasMeasurement(int);
+ method public boolean hasMeasurements(int);
+ method public boolean hasStats(int);
+ method public boolean hasTimer(int);
+ method public boolean hasTimers(int);
+ }
+
+ public final class PackageHealthStats {
+ field public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = 40002; // 0x9c42
+ field public static final int STATS_SERVICES = 40001; // 0x9c41
+ }
+
+ public final class PidHealthStats {
+ field public static final int MEASUREMENT_WAKE_NESTING_COUNT = 20001; // 0x4e21
+ field public static final int MEASUREMENT_WAKE_START_MS = 20003; // 0x4e23
+ field public static final int MEASUREMENT_WAKE_SUM_MS = 20002; // 0x4e22
+ }
+
+ public final class ProcessHealthStats {
+ field public static final int MEASUREMENT_ANR_COUNT = 30005; // 0x7535
+ field public static final int MEASUREMENT_CRASHES_COUNT = 30004; // 0x7534
+ field public static final int MEASUREMENT_FOREGROUND_MS = 30006; // 0x7536
+ field public static final int MEASUREMENT_STARTS_COUNT = 30003; // 0x7533
+ field public static final int MEASUREMENT_SYSTEM_TIME_MS = 30002; // 0x7532
+ field public static final int MEASUREMENT_USER_TIME_MS = 30001; // 0x7531
+ }
+
+ public final class ServiceHealthStats {
+ field public static final int MEASUREMENT_LAUNCH_COUNT = 50002; // 0xc352
+ field public static final int MEASUREMENT_START_SERVICE_COUNT = 50001; // 0xc351
+ }
+
+ public class SystemHealthManager {
+ method public static android.os.health.SystemHealthManager from(android.content.Context);
+ method public android.os.health.HealthStats takeMyUidSnapshot();
+ method public android.os.health.HealthStats takeUidSnapshot(int);
+ method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
+ }
+
+ public class TimerStat implements android.os.Parcelable {
+ ctor public TimerStat();
+ ctor public TimerStat(int, long);
+ ctor public TimerStat(android.os.Parcel);
+ method public int describeContents();
+ method public int getCount();
+ method public long getTime();
+ method public void setCount(int);
+ method public void setTime(long);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.health.TimerStat> CREATOR;
+ }
+
+ public final class UidHealthStats {
+ field public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = 10020; // 0x2724
+ field public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = 10023; // 0x2727
+ field public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = 10052; // 0x2744
+ field public static final int MEASUREMENT_BLUETOOTH_RX_MS = 10021; // 0x2725
+ field public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = 10058; // 0x274a
+ field public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = 10053; // 0x2745
+ field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
+ field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
+ field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
+ field public static final int MEASUREMENT_CPU_POWER_MAUS = 10064; // 0x2750
+ field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
+ field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
+ field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
+ field public static final int MEASUREMENT_MOBILE_RX_MS = 10025; // 0x2729
+ field public static final int MEASUREMENT_MOBILE_RX_PACKETS = 10054; // 0x2746
+ field public static final int MEASUREMENT_MOBILE_TX_BYTES = 10049; // 0x2741
+ field public static final int MEASUREMENT_MOBILE_TX_MS = 10026; // 0x272a
+ field public static final int MEASUREMENT_MOBILE_TX_PACKETS = 10055; // 0x2747
+ field public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = 10045; // 0x273d
+ field public static final int MEASUREMENT_REALTIME_BATTERY_MS = 10001; // 0x2711
+ field public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = 10003; // 0x2713
+ field public static final int MEASUREMENT_SYSTEM_CPU_TIME_US = 10063; // 0x274f
+ field public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = 10047; // 0x273f
+ field public static final int MEASUREMENT_UPTIME_BATTERY_MS = 10002; // 0x2712
+ field public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = 10004; // 0x2714
+ field public static final int MEASUREMENT_USER_CPU_TIME_US = 10062; // 0x274e
+ field public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = 10029; // 0x272d
+ field public static final int MEASUREMENT_WIFI_IDLE_MS = 10016; // 0x2720
+ field public static final int MEASUREMENT_WIFI_MULTICAST_MS = 10031; // 0x272f
+ field public static final int MEASUREMENT_WIFI_POWER_MAMS = 10019; // 0x2723
+ field public static final int MEASUREMENT_WIFI_RUNNING_MS = 10028; // 0x272c
+ field public static final int MEASUREMENT_WIFI_RX_BYTES = 10050; // 0x2742
+ field public static final int MEASUREMENT_WIFI_RX_MS = 10017; // 0x2721
+ field public static final int MEASUREMENT_WIFI_RX_PACKETS = 10056; // 0x2748
+ field public static final int MEASUREMENT_WIFI_TX_BYTES = 10051; // 0x2743
+ field public static final int MEASUREMENT_WIFI_TX_MS = 10018; // 0x2722
+ field public static final int MEASUREMENT_WIFI_TX_PACKETS = 10057; // 0x2749
+ field public static final int STATS_PACKAGES = 10015; // 0x271f
+ field public static final int STATS_PIDS = 10013; // 0x271d
+ field public static final int STATS_PROCESSES = 10014; // 0x271e
+ field public static final int TIMERS_JOBS = 10010; // 0x271a
+ field public static final int TIMERS_SENSORS = 10012; // 0x271c
+ field public static final int TIMERS_SYNCS = 10009; // 0x2719
+ field public static final int TIMERS_WAKELOCKS_DRAW = 10008; // 0x2718
+ field public static final int TIMERS_WAKELOCKS_FULL = 10005; // 0x2715
+ field public static final int TIMERS_WAKELOCKS_PARTIAL = 10006; // 0x2716
+ field public static final int TIMERS_WAKELOCKS_WINDOW = 10007; // 0x2717
+ field public static final int TIMER_AUDIO = 10032; // 0x2730
+ field public static final int TIMER_BLUETOOTH_SCAN = 10037; // 0x2735
+ field public static final int TIMER_CAMERA = 10035; // 0x2733
+ field public static final int TIMER_FLASHLIGHT = 10034; // 0x2732
+ field public static final int TIMER_FOREGROUND_ACTIVITY = 10036; // 0x2734
+ field public static final int TIMER_GPS_SENSOR = 10011; // 0x271b
+ field public static final int TIMER_MOBILE_RADIO_ACTIVE = 10061; // 0x274d
+ field public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = 10042; // 0x273a
+ field public static final int TIMER_PROCESS_STATE_CACHED_MS = 10043; // 0x273b
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = 10041; // 0x2739
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = 10039; // 0x2737
+ field public static final int TIMER_PROCESS_STATE_TOP_MS = 10038; // 0x2736
+ field public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = 10040; // 0x2738
+ field public static final int TIMER_VIBRATOR = 10044; // 0x273c
+ field public static final int TIMER_VIDEO = 10033; // 0x2731
+ field public static final int TIMER_WIFI_SCAN = 10030; // 0x272e
+ }
+
+}
+
package android.os.storage {
public abstract class OnObbStateChangeListener {
@@ -50672,6 +50907,7 @@
public abstract interface Iterable {
method public default void forEach(java.util.function.Consumer<? super T>);
method public abstract java.util.Iterator<T> iterator();
+ method public default java.util.Spliterator<T> spliterator();
}
public class LinkageError extends java.lang.Error {
@@ -57092,6 +57328,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
@@ -57103,6 +57340,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public void trimToSize();
}
@@ -57203,6 +57441,14 @@
method public static void sort(java.lang.Object[], int, int);
method public static void sort(T[], java.util.Comparator<? super T>);
method public static void sort(T[], int, int, java.util.Comparator<? super T>);
+ method public static java.util.Spliterator<T> spliterator(T[]);
+ method public static java.util.Spliterator<T> spliterator(T[], int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[]);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[]);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[]);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
method public static java.lang.String toString(long[]);
method public static java.lang.String toString(int[]);
method public static java.lang.String toString(short[]);
@@ -57702,6 +57948,7 @@
method public java.lang.Object clone();
method public java.util.Iterator<E> iterator();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class Hashtable extends java.util.Dictionary implements java.lang.Cloneable java.util.Map java.io.Serializable {
@@ -57846,6 +58093,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface List implements java.util.Collection {
@@ -58012,6 +58260,8 @@
}
public static abstract interface Map.Entry {
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey(java.util.Comparator<? super K>);
method public abstract boolean equals(java.lang.Object);
method public abstract K getKey();
method public abstract V getValue();
@@ -58190,6 +58440,7 @@
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
+ ctor public PriorityQueue(java.util.Comparator<? super E>);
ctor public PriorityQueue(int, java.util.Comparator<? super E>);
ctor public PriorityQueue(java.util.Collection<? extends E>);
ctor public PriorityQueue(java.util.PriorityQueue<? extends E>);
@@ -58200,6 +58451,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public final java.util.Spliterator<E> spliterator();
}
public class Properties extends java.util.Hashtable {
@@ -58442,6 +58694,111 @@
method public abstract java.util.SortedSet<E> tailSet(E);
}
+ public abstract interface Spliterator {
+ method public abstract int characteristics();
+ method public abstract long estimateSize();
+ method public default void forEachRemaining(java.util.function.Consumer<? super T>);
+ method public default java.util.Comparator<? super T> getComparator();
+ method public default long getExactSizeIfKnown();
+ method public default boolean hasCharacteristics(int);
+ method public abstract boolean tryAdvance(java.util.function.Consumer<? super T>);
+ method public abstract java.util.Spliterator<T> trySplit();
+ field public static final int CONCURRENT = 4096; // 0x1000
+ field public static final int DISTINCT = 1; // 0x1
+ field public static final int IMMUTABLE = 1024; // 0x400
+ field public static final int NONNULL = 256; // 0x100
+ field public static final int ORDERED = 16; // 0x10
+ field public static final int SIZED = 64; // 0x40
+ field public static final int SORTED = 4; // 0x4
+ field public static final int SUBSIZED = 16384; // 0x4000
+ }
+
+ public static abstract interface Spliterator.OfDouble implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract boolean tryAdvance(java.util.function.DoubleConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract interface Spliterator.OfInt implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract boolean tryAdvance(java.util.function.IntConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract interface Spliterator.OfLong implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract boolean tryAdvance(java.util.function.LongConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract interface Spliterator.OfPrimitive implements java.util.Spliterator {
+ method public default void forEachRemaining(T_CONS);
+ method public abstract boolean tryAdvance(T_CONS);
+ method public abstract T_SPLITR trySplit();
+ }
+
+ public final class Spliterators {
+ method public static java.util.Spliterator.OfDouble emptyDoubleSpliterator();
+ method public static java.util.Spliterator.OfInt emptyIntSpliterator();
+ method public static java.util.Spliterator.OfLong emptyLongSpliterator();
+ method public static java.util.Spliterator<T> emptySpliterator();
+ method public static java.util.Iterator<T> iterator(java.util.Spliterator<? extends T>);
+ method public static java.util.PrimitiveIterator.OfInt iterator(java.util.Spliterator.OfInt);
+ method public static java.util.PrimitiveIterator.OfLong iterator(java.util.Spliterator.OfLong);
+ method public static java.util.PrimitiveIterator.OfDouble iterator(java.util.Spliterator.OfDouble);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int, int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Collection<? extends T>, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Iterator<? extends T>, long, int);
+ method public static java.util.Spliterator.OfInt spliterator(java.util.PrimitiveIterator.OfInt, long, int);
+ method public static java.util.Spliterator.OfLong spliterator(java.util.PrimitiveIterator.OfLong, long, int);
+ method public static java.util.Spliterator.OfDouble spliterator(java.util.PrimitiveIterator.OfDouble, long, int);
+ method public static java.util.Spliterator<T> spliteratorUnknownSize(java.util.Iterator<? extends T>, int);
+ method public static java.util.Spliterator.OfInt spliteratorUnknownSize(java.util.PrimitiveIterator.OfInt, int);
+ method public static java.util.Spliterator.OfLong spliteratorUnknownSize(java.util.PrimitiveIterator.OfLong, int);
+ method public static java.util.Spliterator.OfDouble spliteratorUnknownSize(java.util.PrimitiveIterator.OfDouble, int);
+ }
+
+ public static abstract class Spliterators.AbstractDoubleSpliterator implements java.util.Spliterator.OfDouble {
+ ctor protected Spliterators.AbstractDoubleSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractIntSpliterator implements java.util.Spliterator.OfInt {
+ ctor protected Spliterators.AbstractIntSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractLongSpliterator implements java.util.Spliterator.OfLong {
+ ctor protected Spliterators.AbstractLongSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractSpliterator implements java.util.Spliterator {
+ ctor protected Spliterators.AbstractSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator<T> trySplit();
+ }
+
public class Stack extends java.util.Vector {
ctor public Stack();
method public boolean empty();
@@ -58451,6 +58808,15 @@
method public synchronized int search(java.lang.Object);
}
+ public final class StringJoiner {
+ ctor public StringJoiner(java.lang.CharSequence);
+ ctor public StringJoiner(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public java.util.StringJoiner add(java.lang.CharSequence);
+ method public int length();
+ method public java.util.StringJoiner merge(java.util.StringJoiner);
+ method public java.util.StringJoiner setEmptyValue(java.lang.CharSequence);
+ }
+
public class StringTokenizer implements java.util.Enumeration {
ctor public StringTokenizer(java.lang.String, java.lang.String, boolean);
ctor public StringTokenizer(java.lang.String, java.lang.String);
@@ -58572,6 +58938,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.SortedSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -58628,6 +58995,7 @@
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
field protected int elementCount;
diff --git a/api/system-current.txt b/api/system-current.txt
index 568f607..23b9d97 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8448,6 +8448,7 @@
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
@@ -9894,6 +9895,7 @@
method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+ method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
}
@@ -21672,37 +21674,129 @@
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+ field public static final java.lang.String TAG_ARTIST = "Artist";
+ field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+ field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+ field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+ field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+ field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+ field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+ field public static final java.lang.String TAG_COMPRESSION = "Compression";
+ field public static final java.lang.String TAG_CONTRAST = "Contrast";
+ field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+ field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
field public static final java.lang.String TAG_DATETIME = "DateTime";
field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+ field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+ field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+ field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
field public static final java.lang.String TAG_FLASH = "Flash";
+ field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+ field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+ field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+ field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+ field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+ field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+ field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+ field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+ field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+ field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+ field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+ field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+ field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+ field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+ field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+ field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+ field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+ field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+ field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+ field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+ field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
field public static final java.lang.String TAG_MAKE = "Make";
+ field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+ field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
field public static final java.lang.String TAG_MODEL = "Model";
+ field public static final java.lang.String TAG_OECF = "OECF";
field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+ field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+ field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+ field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+ field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+ field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+ field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+ field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+ field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+ field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+ field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+ field public static final java.lang.String TAG_SATURATION = "Saturation";
+ field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+ field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+ field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+ field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+ field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+ field public static final java.lang.String TAG_SOFTWARE = "Software";
+ field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+ field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+ field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+ field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+ field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+ field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+ field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+ field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+ field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+ field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+ field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+ field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+ field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+ field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+ field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+ field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
field public static final int WHITEBALANCE_AUTO = 0; // 0x0
field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
}
@@ -31710,6 +31804,147 @@
}
+package android.os.health {
+
+ public class HealthStats {
+ method public java.lang.String getDataType();
+ method public long getMeasurement(int);
+ method public int getMeasurementKeyAt(int);
+ method public int getMeasurementKeyCount();
+ method public java.util.Map<java.lang.String, java.lang.Long> getMeasurements(int);
+ method public int getMeasurementsKeyAt(int);
+ method public int getMeasurementsKeyCount();
+ method public java.util.Map<java.lang.String, android.os.health.HealthStats> getStats(int);
+ method public int getStatsKeyAt(int);
+ method public int getStatsKeyCount();
+ method public android.os.health.TimerStat getTimer(int);
+ method public int getTimerCount(int);
+ method public int getTimerKeyAt(int);
+ method public int getTimerKeyCount();
+ method public long getTimerTime(int);
+ method public java.util.Map<java.lang.String, android.os.health.TimerStat> getTimers(int);
+ method public int getTimersKeyAt(int);
+ method public int getTimersKeyCount();
+ method public boolean hasMeasurement(int);
+ method public boolean hasMeasurements(int);
+ method public boolean hasStats(int);
+ method public boolean hasTimer(int);
+ method public boolean hasTimers(int);
+ }
+
+ public final class PackageHealthStats {
+ field public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = 40002; // 0x9c42
+ field public static final int STATS_SERVICES = 40001; // 0x9c41
+ }
+
+ public final class PidHealthStats {
+ field public static final int MEASUREMENT_WAKE_NESTING_COUNT = 20001; // 0x4e21
+ field public static final int MEASUREMENT_WAKE_START_MS = 20003; // 0x4e23
+ field public static final int MEASUREMENT_WAKE_SUM_MS = 20002; // 0x4e22
+ }
+
+ public final class ProcessHealthStats {
+ field public static final int MEASUREMENT_ANR_COUNT = 30005; // 0x7535
+ field public static final int MEASUREMENT_CRASHES_COUNT = 30004; // 0x7534
+ field public static final int MEASUREMENT_FOREGROUND_MS = 30006; // 0x7536
+ field public static final int MEASUREMENT_STARTS_COUNT = 30003; // 0x7533
+ field public static final int MEASUREMENT_SYSTEM_TIME_MS = 30002; // 0x7532
+ field public static final int MEASUREMENT_USER_TIME_MS = 30001; // 0x7531
+ }
+
+ public final class ServiceHealthStats {
+ field public static final int MEASUREMENT_LAUNCH_COUNT = 50002; // 0xc352
+ field public static final int MEASUREMENT_START_SERVICE_COUNT = 50001; // 0xc351
+ }
+
+ public class SystemHealthManager {
+ method public static android.os.health.SystemHealthManager from(android.content.Context);
+ method public android.os.health.HealthStats takeMyUidSnapshot();
+ method public android.os.health.HealthStats takeUidSnapshot(int);
+ method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
+ }
+
+ public class TimerStat implements android.os.Parcelable {
+ ctor public TimerStat();
+ ctor public TimerStat(int, long);
+ ctor public TimerStat(android.os.Parcel);
+ method public int describeContents();
+ method public int getCount();
+ method public long getTime();
+ method public void setCount(int);
+ method public void setTime(long);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.health.TimerStat> CREATOR;
+ }
+
+ public final class UidHealthStats {
+ field public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = 10020; // 0x2724
+ field public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = 10023; // 0x2727
+ field public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = 10052; // 0x2744
+ field public static final int MEASUREMENT_BLUETOOTH_RX_MS = 10021; // 0x2725
+ field public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = 10058; // 0x274a
+ field public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = 10053; // 0x2745
+ field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
+ field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
+ field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
+ field public static final int MEASUREMENT_CPU_POWER_MAUS = 10064; // 0x2750
+ field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
+ field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
+ field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
+ field public static final int MEASUREMENT_MOBILE_RX_MS = 10025; // 0x2729
+ field public static final int MEASUREMENT_MOBILE_RX_PACKETS = 10054; // 0x2746
+ field public static final int MEASUREMENT_MOBILE_TX_BYTES = 10049; // 0x2741
+ field public static final int MEASUREMENT_MOBILE_TX_MS = 10026; // 0x272a
+ field public static final int MEASUREMENT_MOBILE_TX_PACKETS = 10055; // 0x2747
+ field public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = 10045; // 0x273d
+ field public static final int MEASUREMENT_REALTIME_BATTERY_MS = 10001; // 0x2711
+ field public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = 10003; // 0x2713
+ field public static final int MEASUREMENT_SYSTEM_CPU_TIME_US = 10063; // 0x274f
+ field public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = 10047; // 0x273f
+ field public static final int MEASUREMENT_UPTIME_BATTERY_MS = 10002; // 0x2712
+ field public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = 10004; // 0x2714
+ field public static final int MEASUREMENT_USER_CPU_TIME_US = 10062; // 0x274e
+ field public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = 10029; // 0x272d
+ field public static final int MEASUREMENT_WIFI_IDLE_MS = 10016; // 0x2720
+ field public static final int MEASUREMENT_WIFI_MULTICAST_MS = 10031; // 0x272f
+ field public static final int MEASUREMENT_WIFI_POWER_MAMS = 10019; // 0x2723
+ field public static final int MEASUREMENT_WIFI_RUNNING_MS = 10028; // 0x272c
+ field public static final int MEASUREMENT_WIFI_RX_BYTES = 10050; // 0x2742
+ field public static final int MEASUREMENT_WIFI_RX_MS = 10017; // 0x2721
+ field public static final int MEASUREMENT_WIFI_RX_PACKETS = 10056; // 0x2748
+ field public static final int MEASUREMENT_WIFI_TX_BYTES = 10051; // 0x2743
+ field public static final int MEASUREMENT_WIFI_TX_MS = 10018; // 0x2722
+ field public static final int MEASUREMENT_WIFI_TX_PACKETS = 10057; // 0x2749
+ field public static final int STATS_PACKAGES = 10015; // 0x271f
+ field public static final int STATS_PIDS = 10013; // 0x271d
+ field public static final int STATS_PROCESSES = 10014; // 0x271e
+ field public static final int TIMERS_JOBS = 10010; // 0x271a
+ field public static final int TIMERS_SENSORS = 10012; // 0x271c
+ field public static final int TIMERS_SYNCS = 10009; // 0x2719
+ field public static final int TIMERS_WAKELOCKS_DRAW = 10008; // 0x2718
+ field public static final int TIMERS_WAKELOCKS_FULL = 10005; // 0x2715
+ field public static final int TIMERS_WAKELOCKS_PARTIAL = 10006; // 0x2716
+ field public static final int TIMERS_WAKELOCKS_WINDOW = 10007; // 0x2717
+ field public static final int TIMER_AUDIO = 10032; // 0x2730
+ field public static final int TIMER_BLUETOOTH_SCAN = 10037; // 0x2735
+ field public static final int TIMER_CAMERA = 10035; // 0x2733
+ field public static final int TIMER_FLASHLIGHT = 10034; // 0x2732
+ field public static final int TIMER_FOREGROUND_ACTIVITY = 10036; // 0x2734
+ field public static final int TIMER_GPS_SENSOR = 10011; // 0x271b
+ field public static final int TIMER_MOBILE_RADIO_ACTIVE = 10061; // 0x274d
+ field public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = 10042; // 0x273a
+ field public static final int TIMER_PROCESS_STATE_CACHED_MS = 10043; // 0x273b
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = 10041; // 0x2739
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = 10039; // 0x2737
+ field public static final int TIMER_PROCESS_STATE_TOP_MS = 10038; // 0x2736
+ field public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = 10040; // 0x2738
+ field public static final int TIMER_VIBRATOR = 10044; // 0x273c
+ field public static final int TIMER_VIDEO = 10033; // 0x2731
+ field public static final int TIMER_WIFI_SCAN = 10030; // 0x272e
+ }
+
+}
+
package android.os.storage {
public abstract class OnObbStateChangeListener {
@@ -53770,6 +54005,7 @@
public abstract interface Iterable {
method public default void forEach(java.util.function.Consumer<? super T>);
method public abstract java.util.Iterator<T> iterator();
+ method public default java.util.Spliterator<T> spliterator();
}
public class LinkageError extends java.lang.Error {
@@ -60190,6 +60426,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
@@ -60201,6 +60438,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public void trimToSize();
}
@@ -60301,6 +60539,14 @@
method public static void sort(java.lang.Object[], int, int);
method public static void sort(T[], java.util.Comparator<? super T>);
method public static void sort(T[], int, int, java.util.Comparator<? super T>);
+ method public static java.util.Spliterator<T> spliterator(T[]);
+ method public static java.util.Spliterator<T> spliterator(T[], int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[]);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[]);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[]);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
method public static java.lang.String toString(long[]);
method public static java.lang.String toString(int[]);
method public static java.lang.String toString(short[]);
@@ -60800,6 +61046,7 @@
method public java.lang.Object clone();
method public java.util.Iterator<E> iterator();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class Hashtable extends java.util.Dictionary implements java.lang.Cloneable java.util.Map java.io.Serializable {
@@ -60944,6 +61191,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface List implements java.util.Collection {
@@ -61110,6 +61358,8 @@
}
public static abstract interface Map.Entry {
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey(java.util.Comparator<? super K>);
method public abstract boolean equals(java.lang.Object);
method public abstract K getKey();
method public abstract V getValue();
@@ -61288,6 +61538,7 @@
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
+ ctor public PriorityQueue(java.util.Comparator<? super E>);
ctor public PriorityQueue(int, java.util.Comparator<? super E>);
ctor public PriorityQueue(java.util.Collection<? extends E>);
ctor public PriorityQueue(java.util.PriorityQueue<? extends E>);
@@ -61298,6 +61549,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public final java.util.Spliterator<E> spliterator();
}
public class Properties extends java.util.Hashtable {
@@ -61540,6 +61792,111 @@
method public abstract java.util.SortedSet<E> tailSet(E);
}
+ public abstract interface Spliterator {
+ method public abstract int characteristics();
+ method public abstract long estimateSize();
+ method public default void forEachRemaining(java.util.function.Consumer<? super T>);
+ method public default java.util.Comparator<? super T> getComparator();
+ method public default long getExactSizeIfKnown();
+ method public default boolean hasCharacteristics(int);
+ method public abstract boolean tryAdvance(java.util.function.Consumer<? super T>);
+ method public abstract java.util.Spliterator<T> trySplit();
+ field public static final int CONCURRENT = 4096; // 0x1000
+ field public static final int DISTINCT = 1; // 0x1
+ field public static final int IMMUTABLE = 1024; // 0x400
+ field public static final int NONNULL = 256; // 0x100
+ field public static final int ORDERED = 16; // 0x10
+ field public static final int SIZED = 64; // 0x40
+ field public static final int SORTED = 4; // 0x4
+ field public static final int SUBSIZED = 16384; // 0x4000
+ }
+
+ public static abstract interface Spliterator.OfDouble implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract boolean tryAdvance(java.util.function.DoubleConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract interface Spliterator.OfInt implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract boolean tryAdvance(java.util.function.IntConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract interface Spliterator.OfLong implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract boolean tryAdvance(java.util.function.LongConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract interface Spliterator.OfPrimitive implements java.util.Spliterator {
+ method public default void forEachRemaining(T_CONS);
+ method public abstract boolean tryAdvance(T_CONS);
+ method public abstract T_SPLITR trySplit();
+ }
+
+ public final class Spliterators {
+ method public static java.util.Spliterator.OfDouble emptyDoubleSpliterator();
+ method public static java.util.Spliterator.OfInt emptyIntSpliterator();
+ method public static java.util.Spliterator.OfLong emptyLongSpliterator();
+ method public static java.util.Spliterator<T> emptySpliterator();
+ method public static java.util.Iterator<T> iterator(java.util.Spliterator<? extends T>);
+ method public static java.util.PrimitiveIterator.OfInt iterator(java.util.Spliterator.OfInt);
+ method public static java.util.PrimitiveIterator.OfLong iterator(java.util.Spliterator.OfLong);
+ method public static java.util.PrimitiveIterator.OfDouble iterator(java.util.Spliterator.OfDouble);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int, int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Collection<? extends T>, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Iterator<? extends T>, long, int);
+ method public static java.util.Spliterator.OfInt spliterator(java.util.PrimitiveIterator.OfInt, long, int);
+ method public static java.util.Spliterator.OfLong spliterator(java.util.PrimitiveIterator.OfLong, long, int);
+ method public static java.util.Spliterator.OfDouble spliterator(java.util.PrimitiveIterator.OfDouble, long, int);
+ method public static java.util.Spliterator<T> spliteratorUnknownSize(java.util.Iterator<? extends T>, int);
+ method public static java.util.Spliterator.OfInt spliteratorUnknownSize(java.util.PrimitiveIterator.OfInt, int);
+ method public static java.util.Spliterator.OfLong spliteratorUnknownSize(java.util.PrimitiveIterator.OfLong, int);
+ method public static java.util.Spliterator.OfDouble spliteratorUnknownSize(java.util.PrimitiveIterator.OfDouble, int);
+ }
+
+ public static abstract class Spliterators.AbstractDoubleSpliterator implements java.util.Spliterator.OfDouble {
+ ctor protected Spliterators.AbstractDoubleSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractIntSpliterator implements java.util.Spliterator.OfInt {
+ ctor protected Spliterators.AbstractIntSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractLongSpliterator implements java.util.Spliterator.OfLong {
+ ctor protected Spliterators.AbstractLongSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractSpliterator implements java.util.Spliterator {
+ ctor protected Spliterators.AbstractSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator<T> trySplit();
+ }
+
public class Stack extends java.util.Vector {
ctor public Stack();
method public boolean empty();
@@ -61549,6 +61906,15 @@
method public synchronized int search(java.lang.Object);
}
+ public final class StringJoiner {
+ ctor public StringJoiner(java.lang.CharSequence);
+ ctor public StringJoiner(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public java.util.StringJoiner add(java.lang.CharSequence);
+ method public int length();
+ method public java.util.StringJoiner merge(java.util.StringJoiner);
+ method public java.util.StringJoiner setEmptyValue(java.lang.CharSequence);
+ }
+
public class StringTokenizer implements java.util.Enumeration {
ctor public StringTokenizer(java.lang.String, java.lang.String, boolean);
ctor public StringTokenizer(java.lang.String, java.lang.String);
@@ -61670,6 +62036,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.SortedSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -61726,6 +62093,7 @@
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
field protected int elementCount;
diff --git a/api/test-current.txt b/api/test-current.txt
index 03dc353..cdffa46 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8148,6 +8148,7 @@
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
@@ -9569,6 +9570,7 @@
method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+ method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
}
@@ -20246,37 +20248,129 @@
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+ field public static final java.lang.String TAG_ARTIST = "Artist";
+ field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+ field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+ field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+ field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+ field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+ field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+ field public static final java.lang.String TAG_COMPRESSION = "Compression";
+ field public static final java.lang.String TAG_CONTRAST = "Contrast";
+ field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+ field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
field public static final java.lang.String TAG_DATETIME = "DateTime";
field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+ field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+ field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+ field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
field public static final java.lang.String TAG_FLASH = "Flash";
+ field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+ field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+ field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+ field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+ field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+ field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+ field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+ field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+ field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+ field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+ field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+ field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+ field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+ field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+ field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+ field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+ field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+ field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+ field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+ field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+ field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
field public static final java.lang.String TAG_MAKE = "Make";
+ field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+ field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
field public static final java.lang.String TAG_MODEL = "Model";
+ field public static final java.lang.String TAG_OECF = "OECF";
field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+ field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+ field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+ field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+ field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+ field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+ field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+ field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+ field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+ field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+ field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+ field public static final java.lang.String TAG_SATURATION = "Saturation";
+ field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+ field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+ field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+ field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+ field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+ field public static final java.lang.String TAG_SOFTWARE = "Software";
+ field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+ field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+ field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+ field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+ field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+ field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+ field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+ field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+ field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+ field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+ field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+ field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+ field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+ field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+ field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+ field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
field public static final int WHITEBALANCE_AUTO = 0; // 0x0
field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
}
@@ -29428,6 +29522,147 @@
}
+package android.os.health {
+
+ public class HealthStats {
+ method public java.lang.String getDataType();
+ method public long getMeasurement(int);
+ method public int getMeasurementKeyAt(int);
+ method public int getMeasurementKeyCount();
+ method public java.util.Map<java.lang.String, java.lang.Long> getMeasurements(int);
+ method public int getMeasurementsKeyAt(int);
+ method public int getMeasurementsKeyCount();
+ method public java.util.Map<java.lang.String, android.os.health.HealthStats> getStats(int);
+ method public int getStatsKeyAt(int);
+ method public int getStatsKeyCount();
+ method public android.os.health.TimerStat getTimer(int);
+ method public int getTimerCount(int);
+ method public int getTimerKeyAt(int);
+ method public int getTimerKeyCount();
+ method public long getTimerTime(int);
+ method public java.util.Map<java.lang.String, android.os.health.TimerStat> getTimers(int);
+ method public int getTimersKeyAt(int);
+ method public int getTimersKeyCount();
+ method public boolean hasMeasurement(int);
+ method public boolean hasMeasurements(int);
+ method public boolean hasStats(int);
+ method public boolean hasTimer(int);
+ method public boolean hasTimers(int);
+ }
+
+ public final class PackageHealthStats {
+ field public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = 40002; // 0x9c42
+ field public static final int STATS_SERVICES = 40001; // 0x9c41
+ }
+
+ public final class PidHealthStats {
+ field public static final int MEASUREMENT_WAKE_NESTING_COUNT = 20001; // 0x4e21
+ field public static final int MEASUREMENT_WAKE_START_MS = 20003; // 0x4e23
+ field public static final int MEASUREMENT_WAKE_SUM_MS = 20002; // 0x4e22
+ }
+
+ public final class ProcessHealthStats {
+ field public static final int MEASUREMENT_ANR_COUNT = 30005; // 0x7535
+ field public static final int MEASUREMENT_CRASHES_COUNT = 30004; // 0x7534
+ field public static final int MEASUREMENT_FOREGROUND_MS = 30006; // 0x7536
+ field public static final int MEASUREMENT_STARTS_COUNT = 30003; // 0x7533
+ field public static final int MEASUREMENT_SYSTEM_TIME_MS = 30002; // 0x7532
+ field public static final int MEASUREMENT_USER_TIME_MS = 30001; // 0x7531
+ }
+
+ public final class ServiceHealthStats {
+ field public static final int MEASUREMENT_LAUNCH_COUNT = 50002; // 0xc352
+ field public static final int MEASUREMENT_START_SERVICE_COUNT = 50001; // 0xc351
+ }
+
+ public class SystemHealthManager {
+ method public static android.os.health.SystemHealthManager from(android.content.Context);
+ method public android.os.health.HealthStats takeMyUidSnapshot();
+ method public android.os.health.HealthStats takeUidSnapshot(int);
+ method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
+ }
+
+ public class TimerStat implements android.os.Parcelable {
+ ctor public TimerStat();
+ ctor public TimerStat(int, long);
+ ctor public TimerStat(android.os.Parcel);
+ method public int describeContents();
+ method public int getCount();
+ method public long getTime();
+ method public void setCount(int);
+ method public void setTime(long);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.health.TimerStat> CREATOR;
+ }
+
+ public final class UidHealthStats {
+ field public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = 10020; // 0x2724
+ field public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = 10023; // 0x2727
+ field public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = 10052; // 0x2744
+ field public static final int MEASUREMENT_BLUETOOTH_RX_MS = 10021; // 0x2725
+ field public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = 10058; // 0x274a
+ field public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = 10053; // 0x2745
+ field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
+ field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
+ field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
+ field public static final int MEASUREMENT_CPU_POWER_MAUS = 10064; // 0x2750
+ field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
+ field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
+ field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
+ field public static final int MEASUREMENT_MOBILE_RX_MS = 10025; // 0x2729
+ field public static final int MEASUREMENT_MOBILE_RX_PACKETS = 10054; // 0x2746
+ field public static final int MEASUREMENT_MOBILE_TX_BYTES = 10049; // 0x2741
+ field public static final int MEASUREMENT_MOBILE_TX_MS = 10026; // 0x272a
+ field public static final int MEASUREMENT_MOBILE_TX_PACKETS = 10055; // 0x2747
+ field public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = 10045; // 0x273d
+ field public static final int MEASUREMENT_REALTIME_BATTERY_MS = 10001; // 0x2711
+ field public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = 10003; // 0x2713
+ field public static final int MEASUREMENT_SYSTEM_CPU_TIME_US = 10063; // 0x274f
+ field public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = 10047; // 0x273f
+ field public static final int MEASUREMENT_UPTIME_BATTERY_MS = 10002; // 0x2712
+ field public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = 10004; // 0x2714
+ field public static final int MEASUREMENT_USER_CPU_TIME_US = 10062; // 0x274e
+ field public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = 10029; // 0x272d
+ field public static final int MEASUREMENT_WIFI_IDLE_MS = 10016; // 0x2720
+ field public static final int MEASUREMENT_WIFI_MULTICAST_MS = 10031; // 0x272f
+ field public static final int MEASUREMENT_WIFI_POWER_MAMS = 10019; // 0x2723
+ field public static final int MEASUREMENT_WIFI_RUNNING_MS = 10028; // 0x272c
+ field public static final int MEASUREMENT_WIFI_RX_BYTES = 10050; // 0x2742
+ field public static final int MEASUREMENT_WIFI_RX_MS = 10017; // 0x2721
+ field public static final int MEASUREMENT_WIFI_RX_PACKETS = 10056; // 0x2748
+ field public static final int MEASUREMENT_WIFI_TX_BYTES = 10051; // 0x2743
+ field public static final int MEASUREMENT_WIFI_TX_MS = 10018; // 0x2722
+ field public static final int MEASUREMENT_WIFI_TX_PACKETS = 10057; // 0x2749
+ field public static final int STATS_PACKAGES = 10015; // 0x271f
+ field public static final int STATS_PIDS = 10013; // 0x271d
+ field public static final int STATS_PROCESSES = 10014; // 0x271e
+ field public static final int TIMERS_JOBS = 10010; // 0x271a
+ field public static final int TIMERS_SENSORS = 10012; // 0x271c
+ field public static final int TIMERS_SYNCS = 10009; // 0x2719
+ field public static final int TIMERS_WAKELOCKS_DRAW = 10008; // 0x2718
+ field public static final int TIMERS_WAKELOCKS_FULL = 10005; // 0x2715
+ field public static final int TIMERS_WAKELOCKS_PARTIAL = 10006; // 0x2716
+ field public static final int TIMERS_WAKELOCKS_WINDOW = 10007; // 0x2717
+ field public static final int TIMER_AUDIO = 10032; // 0x2730
+ field public static final int TIMER_BLUETOOTH_SCAN = 10037; // 0x2735
+ field public static final int TIMER_CAMERA = 10035; // 0x2733
+ field public static final int TIMER_FLASHLIGHT = 10034; // 0x2732
+ field public static final int TIMER_FOREGROUND_ACTIVITY = 10036; // 0x2734
+ field public static final int TIMER_GPS_SENSOR = 10011; // 0x271b
+ field public static final int TIMER_MOBILE_RADIO_ACTIVE = 10061; // 0x274d
+ field public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = 10042; // 0x273a
+ field public static final int TIMER_PROCESS_STATE_CACHED_MS = 10043; // 0x273b
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = 10041; // 0x2739
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = 10039; // 0x2737
+ field public static final int TIMER_PROCESS_STATE_TOP_MS = 10038; // 0x2736
+ field public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = 10040; // 0x2738
+ field public static final int TIMER_VIBRATOR = 10044; // 0x273c
+ field public static final int TIMER_VIDEO = 10033; // 0x2731
+ field public static final int TIMER_WIFI_SCAN = 10030; // 0x272e
+ }
+
+}
+
package android.os.storage {
public abstract class OnObbStateChangeListener {
@@ -50746,6 +50981,7 @@
public abstract interface Iterable {
method public default void forEach(java.util.function.Consumer<? super T>);
method public abstract java.util.Iterator<T> iterator();
+ method public default java.util.Spliterator<T> spliterator();
}
public class LinkageError extends java.lang.Error {
@@ -57166,6 +57402,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
@@ -57177,6 +57414,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public void trimToSize();
}
@@ -57277,6 +57515,14 @@
method public static void sort(java.lang.Object[], int, int);
method public static void sort(T[], java.util.Comparator<? super T>);
method public static void sort(T[], int, int, java.util.Comparator<? super T>);
+ method public static java.util.Spliterator<T> spliterator(T[]);
+ method public static java.util.Spliterator<T> spliterator(T[], int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[]);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[]);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[]);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
method public static java.lang.String toString(long[]);
method public static java.lang.String toString(int[]);
method public static java.lang.String toString(short[]);
@@ -57776,6 +58022,7 @@
method public java.lang.Object clone();
method public java.util.Iterator<E> iterator();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class Hashtable extends java.util.Dictionary implements java.lang.Cloneable java.util.Map java.io.Serializable {
@@ -57920,6 +58167,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface List implements java.util.Collection {
@@ -58086,6 +58334,8 @@
}
public static abstract interface Map.Entry {
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey(java.util.Comparator<? super K>);
method public abstract boolean equals(java.lang.Object);
method public abstract K getKey();
method public abstract V getValue();
@@ -58264,6 +58514,7 @@
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
+ ctor public PriorityQueue(java.util.Comparator<? super E>);
ctor public PriorityQueue(int, java.util.Comparator<? super E>);
ctor public PriorityQueue(java.util.Collection<? extends E>);
ctor public PriorityQueue(java.util.PriorityQueue<? extends E>);
@@ -58274,6 +58525,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public final java.util.Spliterator<E> spliterator();
}
public class Properties extends java.util.Hashtable {
@@ -58516,6 +58768,111 @@
method public abstract java.util.SortedSet<E> tailSet(E);
}
+ public abstract interface Spliterator {
+ method public abstract int characteristics();
+ method public abstract long estimateSize();
+ method public default void forEachRemaining(java.util.function.Consumer<? super T>);
+ method public default java.util.Comparator<? super T> getComparator();
+ method public default long getExactSizeIfKnown();
+ method public default boolean hasCharacteristics(int);
+ method public abstract boolean tryAdvance(java.util.function.Consumer<? super T>);
+ method public abstract java.util.Spliterator<T> trySplit();
+ field public static final int CONCURRENT = 4096; // 0x1000
+ field public static final int DISTINCT = 1; // 0x1
+ field public static final int IMMUTABLE = 1024; // 0x400
+ field public static final int NONNULL = 256; // 0x100
+ field public static final int ORDERED = 16; // 0x10
+ field public static final int SIZED = 64; // 0x40
+ field public static final int SORTED = 4; // 0x4
+ field public static final int SUBSIZED = 16384; // 0x4000
+ }
+
+ public static abstract interface Spliterator.OfDouble implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract boolean tryAdvance(java.util.function.DoubleConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract interface Spliterator.OfInt implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract boolean tryAdvance(java.util.function.IntConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract interface Spliterator.OfLong implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract boolean tryAdvance(java.util.function.LongConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract interface Spliterator.OfPrimitive implements java.util.Spliterator {
+ method public default void forEachRemaining(T_CONS);
+ method public abstract boolean tryAdvance(T_CONS);
+ method public abstract T_SPLITR trySplit();
+ }
+
+ public final class Spliterators {
+ method public static java.util.Spliterator.OfDouble emptyDoubleSpliterator();
+ method public static java.util.Spliterator.OfInt emptyIntSpliterator();
+ method public static java.util.Spliterator.OfLong emptyLongSpliterator();
+ method public static java.util.Spliterator<T> emptySpliterator();
+ method public static java.util.Iterator<T> iterator(java.util.Spliterator<? extends T>);
+ method public static java.util.PrimitiveIterator.OfInt iterator(java.util.Spliterator.OfInt);
+ method public static java.util.PrimitiveIterator.OfLong iterator(java.util.Spliterator.OfLong);
+ method public static java.util.PrimitiveIterator.OfDouble iterator(java.util.Spliterator.OfDouble);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int, int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Collection<? extends T>, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Iterator<? extends T>, long, int);
+ method public static java.util.Spliterator.OfInt spliterator(java.util.PrimitiveIterator.OfInt, long, int);
+ method public static java.util.Spliterator.OfLong spliterator(java.util.PrimitiveIterator.OfLong, long, int);
+ method public static java.util.Spliterator.OfDouble spliterator(java.util.PrimitiveIterator.OfDouble, long, int);
+ method public static java.util.Spliterator<T> spliteratorUnknownSize(java.util.Iterator<? extends T>, int);
+ method public static java.util.Spliterator.OfInt spliteratorUnknownSize(java.util.PrimitiveIterator.OfInt, int);
+ method public static java.util.Spliterator.OfLong spliteratorUnknownSize(java.util.PrimitiveIterator.OfLong, int);
+ method public static java.util.Spliterator.OfDouble spliteratorUnknownSize(java.util.PrimitiveIterator.OfDouble, int);
+ }
+
+ public static abstract class Spliterators.AbstractDoubleSpliterator implements java.util.Spliterator.OfDouble {
+ ctor protected Spliterators.AbstractDoubleSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractIntSpliterator implements java.util.Spliterator.OfInt {
+ ctor protected Spliterators.AbstractIntSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractLongSpliterator implements java.util.Spliterator.OfLong {
+ ctor protected Spliterators.AbstractLongSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractSpliterator implements java.util.Spliterator {
+ ctor protected Spliterators.AbstractSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator<T> trySplit();
+ }
+
public class Stack extends java.util.Vector {
ctor public Stack();
method public boolean empty();
@@ -58525,6 +58882,15 @@
method public synchronized int search(java.lang.Object);
}
+ public final class StringJoiner {
+ ctor public StringJoiner(java.lang.CharSequence);
+ ctor public StringJoiner(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public java.util.StringJoiner add(java.lang.CharSequence);
+ method public int length();
+ method public java.util.StringJoiner merge(java.util.StringJoiner);
+ method public java.util.StringJoiner setEmptyValue(java.lang.CharSequence);
+ }
+
public class StringTokenizer implements java.util.Enumeration {
ctor public StringTokenizer(java.lang.String, java.lang.String, boolean);
ctor public StringTokenizer(java.lang.String, java.lang.String);
@@ -58646,6 +59012,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.SortedSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -58702,6 +59069,7 @@
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
field protected int elementCount;
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 455f869..b08142a 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -985,11 +985,16 @@
/**
* Gets information about the next alarm clock currently scheduled.
*
- * The alarm clocks considered are those scheduled by {@link #setAlarmClock}
- * from any package of the calling user.
+ * The alarm clocks considered are those scheduled by any application
+ * using the {@link #setAlarmClock} method.
+ *
+ * @return An {@link AlarmClockInfo} object describing the next upcoming alarm
+ * clock event that will occur. If there are no alarm clock events currently
+ * scheduled, this method will return {@code null}.
*
* @see #setAlarmClock
* @see AlarmClockInfo
+ * @see #ACTION_NEXT_ALARM_CLOCK_CHANGED
*/
public AlarmClockInfo getNextAlarmClock() {
return getNextAlarmClock(UserHandle.myUserId());
@@ -998,11 +1003,16 @@
/**
* Gets information about the next alarm clock currently scheduled.
*
- * The alarm clocks considered are those scheduled by {@link #setAlarmClock}
- * from any package of the given {@parm userId}.
+ * The alarm clocks considered are those scheduled by any application
+ * using the {@link #setAlarmClock} method within the given user.
+ *
+ * @return An {@link AlarmClockInfo} object describing the next upcoming alarm
+ * clock event that will occur within the given user. If there are no alarm clock
+ * events currently scheduled in that user, this method will return {@code null}.
*
* @see #setAlarmClock
* @see AlarmClockInfo
+ * @see #ACTION_NEXT_ALARM_CLOCK_CHANGED
*
* @hide
*/
@@ -1015,7 +1025,7 @@
}
/**
- * An immutable description of an alarm clock.
+ * An immutable description of a scheduled "alarm clock" event.
*
* @see AlarmManager#setAlarmClock
* @see AlarmManager#getNextAlarmClock
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5affddc..6c0b69c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -103,6 +103,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
+import android.os.health.SystemHealthManager;
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
@@ -747,7 +748,6 @@
@Override
public SoundTriggerManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE);
- Log.i(TAG, "Creating new instance of SoundTriggerManager object.");
return new SoundTriggerManager(ctx, ISoundTriggerService.Stub.asInterface(b));
}});
@@ -758,6 +758,13 @@
IBinder b = ServiceManager.getService(Context.SHORTCUT_SERVICE);
return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
}});
+
+ registerService(Context.SYSTEM_HEALTH_SERVICE, SystemHealthManager.class,
+ new CachedServiceFetcher<SystemHealthManager>() {
+ @Override
+ public SystemHealthManager createService(ContextImpl ctx) {
+ return new SystemHealthManager();
+ }});
}
/**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f96ddf0..825dd5b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3585,6 +3585,15 @@
public static final String SHORTCUT_SERVICE = "shortcut";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.os.health.SystemHealthManager} for accessing system health (battery, power,
+ * memory, etc) metrics.
+ *
+ * @see #getSystemService
+ */
+ public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0dc856c..f80f362 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1596,9 +1596,9 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: If this feature is supported, the Vulkan native API will enumerate
- * at least one {@code VkPhysicalDevice}, and the feature version will indicate what
- * level of optional hardware features limits it supports.
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan native API
+ * will enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate
+ * what level of optional hardware features limits it supports.
* <p>
* Level 0 includes the base Vulkan requirements as well as:
* <ul><li>{@code VkPhysicalDeviceFeatures::textureCompressionETC2}</li></ul>
@@ -1623,7 +1623,7 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The version of this feature indicates the highest
+ * {@link #hasSystemFeature(String, int)}: The version of this feature indicates the highest
* {@code VkPhysicalDeviceProperties::apiVersion} supported by the physical devices that support
* the hardware level indicated by {@link #FEATURE_VULKAN_HARDWARE_LEVEL}. The feature version
* uses the same encoding as Vulkan version numbers:
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index d58ad22..4add962 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2070,6 +2070,24 @@
*/
public static final int CONTROL_SCENE_MODE_FACE_PRIORITY_LOW_LIGHT = 19;
+ /**
+ * <p>Scene mode values within the range of
+ * <code>[DEVICE_CUSTOM_START, DEVICE_CUSTOM_END]</code> are reserved for device specific
+ * customized scene modes.</p>
+ * @see CaptureRequest#CONTROL_SCENE_MODE
+ * @hide
+ */
+ public static final int CONTROL_SCENE_MODE_DEVICE_CUSTOM_START = 100;
+
+ /**
+ * <p>Scene mode values within the range of
+ * <code>[DEVICE_CUSTOM_START, DEVICE_CUSTOM_END]</code> are reserved for device specific
+ * customized scene modes.</p>
+ * @see CaptureRequest#CONTROL_SCENE_MODE
+ * @hide
+ */
+ public static final int CONTROL_SCENE_MODE_DEVICE_CUSTOM_END = 127;
+
//
// Enumeration values for CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
//
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 8281279..e1c7ad77 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -465,6 +465,7 @@
};
public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
+ public abstract Timer getProcessStateTimer(int state);
public abstract Timer getVibratorOnTimer();
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index 838279b..082194b 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -25,10 +25,12 @@
void removePowerSaveWhitelistApp(String name);
String[] getSystemPowerWhitelistExceptIdle();
String[] getSystemPowerWhitelist();
+ String[] getUserPowerWhitelist();
String[] getFullPowerWhitelistExceptIdle();
String[] getFullPowerWhitelist();
int[] getAppIdWhitelistExceptIdle();
int[] getAppIdWhitelist();
+ int[] getAppIdUserWhitelist();
int[] getAppIdTempWhitelist();
boolean isPowerSaveWhitelistExceptIdleApp(String name);
boolean isPowerSaveWhitelistApp(String name);
diff --git a/core/java/android/os/health/HealthKeys.java b/core/java/android/os/health/HealthKeys.java
new file mode 100644
index 0000000..842def3
--- /dev/null
+++ b/core/java/android/os/health/HealthKeys.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+/**
+ * Constants and stuff for the android.os.health package.
+ *
+ * @hide
+ */
+public class HealthKeys {
+
+ /**
+ * No valid key will ever be 0.
+ */
+ public static final int UNKNOWN_KEY = 0;
+
+ /*
+ * Base key for each of the different classes. There is
+ * nothing intrinsic to the operation of the value of the
+ * keys. It's just segmented for better debugging. The
+ * classes don't mix them anway.
+ */
+ public static final int BASE_UID = 10000;
+ public static final int BASE_PID = 20000;
+ public static final int BASE_PROCESS = 30000;
+ public static final int BASE_PACKAGE = 40000;
+ public static final int BASE_SERVICE = 50000;
+
+ /*
+ * The types of values supported by HealthStats.
+ */
+ public static final int TYPE_TIMER = 0;
+ public static final int TYPE_MEASUREMENT = 1;
+ public static final int TYPE_STATS = 2;
+ public static final int TYPE_TIMERS = 3;
+ public static final int TYPE_MEASUREMENTS = 4;
+
+ public static final int TYPE_COUNT = 5;
+
+ /**
+ * Annotation to mark public static final int fields that are to be used
+ * as field keys in HealthStats.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD})
+ public @interface Constant {
+ /**
+ * One of the TYPE_* constants above.
+ */
+ int type();
+ }
+
+ /**
+ * Class to gather the constants defined in a class full of constants and
+ * build the key indices used by HealthStatsWriter and HealthStats.
+ *
+ * @hide
+ */
+ public static class Constants {
+ private final String mDataType;
+ private final int[][] mKeys = new int[TYPE_COUNT][];
+
+ /**
+ * Pass in a class to gather the public static final int fields that are
+ * tagged with the @Constant annotation.
+ */
+ public Constants(Class clazz) {
+ // Save the class name for debugging
+ mDataType = clazz.getSimpleName();
+
+ // Iterate through the list of fields on this class, and build the
+ // constant arrays for these fields.
+ final Field[] fields = clazz.getDeclaredFields();
+ final Class<Constant> annotationClass = Constant.class;
+
+ final int N = fields.length;
+
+ final SortedIntArray[] keys = new SortedIntArray[mKeys.length];
+ for (int i=0; i<keys.length; i++) {
+ keys[i] = new SortedIntArray(N);
+ }
+
+ for (int i=0; i<N; i++) {
+ final Field field = fields[i];
+ final Constant constant = field.getAnnotation(annotationClass);
+ if (constant != null) {
+ final int type = constant.type();
+ if (type >= keys.length) {
+ throw new RuntimeException("Unknown Constant type " + type
+ + " on " + field);
+ }
+ try {
+ keys[type].addValue(field.getInt(null));
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException("Can't read constant value type=" + type
+ + " field=" + field, ex);
+ }
+ }
+ }
+
+ for (int i=0; i<keys.length; i++) {
+ mKeys[i] = keys[i].getArray();
+ }
+ }
+
+ /**
+ * Get a string representation of this class. Useful for debugging. It will be the
+ * simple name of the class passed in the constructor.
+ */
+ public String getDataType() {
+ return mDataType;
+ }
+
+ /**
+ * Return how many keys there are for the given field type.
+ *
+ * @see TYPE_TIMER
+ * @see TYPE_MEASUREMENT
+ * @see TYPE_TIMERS
+ * @see TYPE_MEASUREMENTS
+ * @see TYPE_STATS
+ */
+ public int getSize(int type) {
+ return mKeys[type].length;
+ }
+
+ /**
+ * Return the index for the given type and key combination in the array of field
+ * keys or values.
+ *
+ * @see TYPE_TIMER
+ * @see TYPE_MEASUREMENT
+ * @see TYPE_TIMERS
+ * @see TYPE_MEASUREMENTS
+ * @see TYPE_STATS
+ */
+ public int getIndex(int type, int key) {
+ final int index = Arrays.binarySearch(mKeys[type], key);
+ if (index >= 0) {
+ return index;
+ } else {
+ throw new RuntimeException("Unknown Constant " + key + " (of type "
+ + type + " )");
+ }
+ }
+
+ /**
+ * Get the array of keys for the given field type.
+ */
+ public int[] getKeys(int type) {
+ return mKeys[type];
+ }
+ }
+
+ /**
+ * An array of fixed size that will be sorted.
+ */
+ private static class SortedIntArray {
+ int mCount;
+ int[] mArray;
+
+ /**
+ * Construct with the maximum number of values.
+ */
+ SortedIntArray(int maxCount) {
+ mArray = new int[maxCount];
+ }
+
+ /**
+ * Add a value.
+ */
+ void addValue(int value) {
+ mArray[mCount++] = value;
+ }
+
+ /**
+ * Get the array of values that have been added, with the values in
+ * numerically increasing order.
+ */
+ int[] getArray() {
+ if (mCount == mArray.length) {
+ Arrays.sort(mArray);
+ return mArray;
+ } else {
+ final int[] result = new int[mCount];
+ System.arraycopy(mArray, 0, result, 0, mCount);
+ Arrays.sort(result);
+ return result;
+ }
+ }
+ }
+}
+
+
diff --git a/core/java/android/os/health/HealthStats.java b/core/java/android/os/health/HealthStats.java
new file mode 100644
index 0000000..f0489e2
--- /dev/null
+++ b/core/java/android/os/health/HealthStats.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * A HealthStats object contains system health data about an application.
+ *
+ * <p>
+ * <b>Data Types</b><br>
+ * Each of the keys references data in one of five data types:
+ *
+ * <p>
+ * A <b>measurement</b> metric contains a sinlge {@code long} value. That value may
+ * be a count, a time, or some other type of value. The unit for a measurement
+ * (COUNT, MS, etc) will always be in the name of the constant for the key to
+ * retrieve it. For example, the
+ * {@link android.os.health.UidHealthStats#MEASUREMENT_WIFI_TX_MS UidHealthStats.MEASUREMENT_WIFI_TX_MS}
+ * value is the number of milliseconds (ms) that were spent transmitting on wifi by an
+ * application. The
+ * {@link android.os.health.UidHealthStats#MEASUREMENT_MOBILE_RX_PACKETS UidHealthStats.MEASUREMENT_MOBILE_RX_PACKETS}
+ * measurement is the number of packets received on behalf of an application.
+ * The {@link android.os.health.UidHealthStats#MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT
+ * UidHealthStats.MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT}
+ * measurement is the number of times the user touched the screen, causing the
+ * screen to stay awake.
+ *
+ *
+ * <p>
+ * A <b>timer</b> metric contains an {@code int} count and a {@code long} time,
+ * measured in milliseconds. Timers track how many times a resource was used, and
+ * the total duration for that usage. For example, the
+ * {@link android.os.health.UidHealthStats#TIMER_FLASHLIGHT}
+ * timer tracks how many times the application turned on the flashlight, and for
+ * how many milliseconds total it kept it on.
+ *
+ * <p>
+ * A <b>measurement map</b> metric is a mapping of {@link java.lang.String} names to
+ * {@link java.lang.Long} values. The names typically are application provided names. For
+ * example, the
+ * {@link android.os.health.PackageHealthStats#MEASUREMENTS_WAKEUP_ALARMS_COUNT
+ * PackageHealthStats.MEASUREMENTS_WAKEUP_ALARMS_COUNT}
+ * measurement map is a mapping of the tag provided to the
+ * {@link android.app.AlarmManager} when the alarm is scheduled.
+ *
+ * <p>
+ * A <b>timer map</b> metric is a mapping of {@link java.lang.String} names to
+ * {@link android.os.health.TimerStat} objects. The names are typically application
+ * provided names. For example, the
+ * {@link android.os.health.UidHealthStats#TIMERS_WAKELOCKS_PARTIAL UidHealthStats.TIMERS_WAKELOCKS_PARTIAL}
+ * is a mapping of tag provided to the {@link android.os.PowerManager} when the
+ * wakelock is created to the number of times and for how long each wakelock was
+ * active.
+ *
+ * <p>
+ * Lastly, a <b>health stats</b> metric is a mapping of {@link java.lang.String}
+ * names to a recursive {@link android.os.health.HealthStats} object containing
+ * more detailed information. For example, the
+ * {@link android.os.health.UidHealthStats#STATS_PACKAGES UidHealthStats.STATS_PACKAGES}
+ * metric is a mapping of the package names for each of the APKs sharing a uid to
+ * the information recorded for that apk. The returned HealthStats objects will
+ * each be associated with a different set of constants. For the HealthStats
+ * returned for UidHealthStats.STATS_PACKAGES, the keys come from the
+ * {@link android.os.health.PackageHealthStats} class.
+ *
+ */
+public class HealthStats {
+ // Header fields
+ private String mDataType;
+
+ // TimerStat fields
+ private int[] mTimerKeys;
+ private int[] mTimerCounts;
+ private long[] mTimerTimes;
+
+ // Measurement fields
+ private int[] mMeasurementKeys;
+ private long[] mMeasurementValues;
+
+ // Stats fields
+ private int[] mStatsKeys;
+ private ArrayMap<String,HealthStats>[] mStatsValues;
+
+ // Timers fields
+ private int[] mTimersKeys;
+ private ArrayMap<String,TimerStat>[] mTimersValues;
+
+ // Measurements fields
+ private int[] mMeasurementsKeys;
+ private ArrayMap<String,Long>[] mMeasurementsValues;
+
+ /**
+ * HealthStats empty constructor not implemented because this
+ * class is read-only.
+ */
+ private HealthStats() {
+ throw new RuntimeException("unsupported");
+ }
+
+ /**
+ * Construct a health stats object from a parcel.
+ *
+ * @hide
+ */
+ public HealthStats(Parcel in) {
+ int count;
+
+ // Header fields
+ mDataType = in.readString();
+
+ // TimerStat fields
+ count = in.readInt();
+ mTimerKeys = new int[count];
+ mTimerCounts = new int[count];
+ mTimerTimes = new long[count];
+ for (int i=0; i<count; i++) {
+ mTimerKeys[i] = in.readInt();
+ mTimerCounts[i] = in.readInt();
+ mTimerTimes[i] = in.readLong();
+ }
+
+ // Measurement fields
+ count = in.readInt();
+ mMeasurementKeys = new int[count];
+ mMeasurementValues = new long[count];
+ for (int i=0; i<count; i++) {
+ mMeasurementKeys[i] = in.readInt();
+ mMeasurementValues[i] = in.readLong();
+ }
+
+ // Stats fields
+ count = in.readInt();
+ mStatsKeys = new int[count];
+ mStatsValues = new ArrayMap[count];
+ for (int i=0; i<count; i++) {
+ mStatsKeys[i] = in.readInt();
+ mStatsValues[i] = createHealthStatsMap(in);
+ }
+
+ // Timers fields
+ count = in.readInt();
+ mTimersKeys = new int[count];
+ mTimersValues = new ArrayMap[count];
+ for (int i=0; i<count; i++) {
+ mTimersKeys[i] = in.readInt();
+ mTimersValues[i] = createParcelableMap(in, TimerStat.CREATOR);
+ }
+
+ // Measurements fields
+ count = in.readInt();
+ mMeasurementsKeys = new int[count];
+ mMeasurementsValues = new ArrayMap[count];
+ for (int i=0; i<count; i++) {
+ mMeasurementsKeys[i] = in.readInt();
+ mMeasurementsValues[i] = createLongsMap(in);
+ }
+ }
+
+ /**
+ * Get a name representing the contents of this object.
+ *
+ * @see UidHealthStats
+ * @see PackageHealthStats
+ * @see PidHealthStats
+ * @see ProcessHealthStats
+ * @see ServiceHealthStats
+ */
+ public String getDataType() {
+ return mDataType;
+ }
+
+ /**
+ * Return whether this object contains a TimerStat for the supplied key.
+ */
+ public boolean hasTimer(int key) {
+ return getIndex(mTimerKeys, key) >= 0;
+ }
+
+ /**
+ * Return a TimerStat object for the given key.
+ *
+ * This will allocate a new {@link TimerStat} object, which may be wasteful. Instead, use
+ * {@link #getTimerCount} and {@link #getTimerTime}.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+ */
+ public TimerStat getTimer(int key) {
+ final int index = getIndex(mTimerKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return new TimerStat(mTimerCounts[index], mTimerTimes[index]);
+ }
+
+ /**
+ * Get the count for the timer for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+ */
+ public int getTimerCount(int key) {
+ final int index = getIndex(mTimerKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mTimerCounts[index];
+ }
+
+ /**
+ * Get the time for the timer for the given key, in milliseconds.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+ */
+ public long getTimerTime(int key) {
+ final int index = getIndex(mTimerKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mTimerTimes[index];
+ }
+
+ /**
+ * Get the number of timer values in this object. Can be used to iterate through
+ * the available timers.
+ *
+ * @see #getTimerKeyAt
+ */
+ public int getTimerKeyCount() {
+ return mTimerKeys.length;
+ }
+
+ /**
+ * Get the key for the timer at the given index. Index must be between 0 and the result
+ * of {@link #getTimerKeyCount getTimerKeyCount()}.
+ *
+ * @see #getTimerKeyCount
+ */
+ public int getTimerKeyAt(int index) {
+ return mTimerKeys[index];
+ }
+
+ /**
+ * Return whether this object contains a measurement for the supplied key.
+ */
+ public boolean hasMeasurement(int key) {
+ return getIndex(mMeasurementKeys, key) >= 0;
+ }
+
+ /**
+ * Get the measurement for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasMeasurement hasMeasurement(int) To check if a value for the given key is present.
+ */
+ public long getMeasurement(int key) {
+ final int index = getIndex(mMeasurementKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad measurement key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mMeasurementValues[index];
+ }
+
+ /**
+ * Get the number of measurement values in this object. Can be used to iterate through
+ * the available measurements.
+ *
+ * @see #getMeasurementKeyAt
+ */
+ public int getMeasurementKeyCount() {
+ return mMeasurementKeys.length;
+ }
+
+ /**
+ * Get the key for the measurement at the given index. Index must be between 0 and the result
+ * of {@link #getMeasurementKeyCount getMeasurementKeyCount()}.
+ *
+ * @see #getMeasurementKeyCount
+ */
+ public int getMeasurementKeyAt(int index) {
+ return mMeasurementKeys[index];
+ }
+
+ /**
+ * Return whether this object contains a HealthStats map for the supplied key.
+ */
+ public boolean hasStats(int key) {
+ return getIndex(mStatsKeys, key) >= 0;
+ }
+
+ /**
+ * Get the HealthStats map for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasStats hasStats(int) To check if a value for the given key is present.
+ */
+ public Map<String,HealthStats> getStats(int key) {
+ final int index = getIndex(mStatsKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad stats key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mStatsValues[index];
+ }
+
+ /**
+ * Get the number of HealthStat map values in this object. Can be used to iterate through
+ * the available measurements.
+ *
+ * @see #getMeasurementKeyAt
+ */
+ public int getStatsKeyCount() {
+ return mStatsKeys.length;
+ }
+
+ /**
+ * Get the key for the timer at the given index. Index must be between 0 and the result
+ * of {@link #getStatsKeyCount getStatsKeyCount()}.
+ *
+ * @see #getStatsKeyCount
+ */
+ public int getStatsKeyAt(int index) {
+ return mStatsKeys[index];
+ }
+
+ /**
+ * Return whether this object contains a timers map for the supplied key.
+ */
+ public boolean hasTimers(int key) {
+ return getIndex(mTimersKeys, key) >= 0;
+ }
+
+ /**
+ * Get the TimerStat map for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasTimers hasTimers(int) To check if a value for the given key is present.
+ */
+ public Map<String,TimerStat> getTimers(int key) {
+ final int index = getIndex(mTimersKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad timers key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mTimersValues[index];
+ }
+
+ /**
+ * Get the number of timer map values in this object. Can be used to iterate through
+ * the available timer maps.
+ *
+ * @see #getTimersKeyAt
+ */
+ public int getTimersKeyCount() {
+ return mTimersKeys.length;
+ }
+
+ /**
+ * Get the key for the timer map at the given index. Index must be between 0 and the result
+ * of {@link #getTimersKeyCount getTimersKeyCount()}.
+ *
+ * @see #getTimersKeyCount
+ */
+ public int getTimersKeyAt(int index) {
+ return mTimersKeys[index];
+ }
+
+ /**
+ * Return whether this object contains a measurements map for the supplied key.
+ */
+ public boolean hasMeasurements(int key) {
+ return getIndex(mMeasurementsKeys, key) >= 0;
+ }
+
+ /**
+ * Get the measurements map for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasMeasurements To check if a value for the given key is present.
+ */
+ public Map<String,Long> getMeasurements(int key) {
+ final int index = getIndex(mMeasurementsKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad measurements key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mMeasurementsValues[index];
+ }
+
+ /**
+ * Get the number of measurement map values in this object. Can be used to iterate through
+ * the available measurement maps.
+ *
+ * @see #getMeasurementsKeyAt
+ */
+ public int getMeasurementsKeyCount() {
+ return mMeasurementsKeys.length;
+ }
+
+ /**
+ * Get the key for the measurement map at the given index.
+ * Index must be between 0 and the result
+ * of {@link #getMeasurementsKeyCount getMeasurementsKeyCount()}.
+ *
+ * @see #getMeasurementsKeyCount
+ */
+ public int getMeasurementsKeyAt(int index) {
+ return mMeasurementsKeys[index];
+ }
+
+ /**
+ * Get the index in keys of key.
+ */
+ private static int getIndex(int[] keys, int key) {
+ return Arrays.binarySearch(keys, key);
+ }
+
+ /**
+ * Create an ArrayMap<String,HealthStats> from the given Parcel.
+ */
+ private static ArrayMap<String,HealthStats> createHealthStatsMap(Parcel in) {
+ final int count = in.readInt();
+ final ArrayMap<String,HealthStats> result = new ArrayMap<String,HealthStats>(count);
+ for (int i=0; i<count; i++) {
+ result.put(in.readString(), new HealthStats(in));
+ }
+ return result;
+ }
+
+ /**
+ * Create an ArrayMap<String,T extends Parcelable> from the given Parcel using
+ * the given Parcelable.Creator.
+ */
+ private static <T extends Parcelable> ArrayMap<String,T> createParcelableMap(Parcel in,
+ Parcelable.Creator<T> creator) {
+ final int count = in.readInt();
+ final ArrayMap<String,T> result = new ArrayMap<String,T>(count);
+ for (int i=0; i<count; i++) {
+ result.put(in.readString(), creator.createFromParcel(in));
+ }
+ return result;
+ }
+
+ /**
+ * Create an ArrayMap<String,Long> from the given Parcel.
+ */
+ private static ArrayMap<String,Long> createLongsMap(Parcel in) {
+ final int count = in.readInt();
+ final ArrayMap<String,Long> result = new ArrayMap<String,Long>(count);
+ for (int i=0; i<count; i++) {
+ result.put(in.readString(), in.readLong());
+ }
+ return result;
+ }
+}
+
diff --git a/core/java/android/os/health/HealthStatsParceler.aidl b/core/java/android/os/health/HealthStatsParceler.aidl
new file mode 100644
index 0000000..68c348b
--- /dev/null
+++ b/core/java/android/os/health/HealthStatsParceler.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+parcelable HealthStatsParceler;
diff --git a/core/java/android/os/health/HealthStatsParceler.java b/core/java/android/os/health/HealthStatsParceler.java
new file mode 100644
index 0000000..28b3694
--- /dev/null
+++ b/core/java/android/os/health/HealthStatsParceler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Class to allow sending the HealthStats through aidl generated glue.
+ *
+ * The alternative would be to send a HealthStats object, which would
+ * require constructing one, and then immediately flattening it. This
+ * saves that step at the cost of doing the extra flattening when
+ * accessed in the same process as the writer.
+ *
+ * The HealthStatsWriter passed in the constructor is retained, so don't
+ * reuse them.
+ * @hide
+ */
+public class HealthStatsParceler implements Parcelable {
+ private HealthStatsWriter mWriter;
+ private HealthStats mHealthStats;
+
+ public static final Parcelable.Creator<HealthStatsParceler> CREATOR
+ = new Parcelable.Creator<HealthStatsParceler>() {
+ public HealthStatsParceler createFromParcel(Parcel in) {
+ return new HealthStatsParceler(in);
+ }
+
+ public HealthStatsParceler[] newArray(int size) {
+ return new HealthStatsParceler[size];
+ }
+ };
+
+ public HealthStatsParceler(HealthStatsWriter writer) {
+ mWriter = writer;
+ }
+
+ public HealthStatsParceler(Parcel in) {
+ mHealthStats = new HealthStats(in);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ // See comment on mWriter declaration above.
+ if (mWriter != null) {
+ mWriter.flattenToParcel(out);
+ } else {
+ throw new RuntimeException("Can not re-parcel HealthStatsParceler that was"
+ + " constructed from a Parcel");
+ }
+ }
+
+ public HealthStats getHealthStats() {
+ if (mWriter != null) {
+ final Parcel parcel = Parcel.obtain();
+ mWriter.flattenToParcel(parcel);
+ parcel.setDataPosition(0);
+ mHealthStats = new HealthStats(parcel);
+ parcel.recycle();
+ }
+
+ return mHealthStats;
+ }
+}
+
diff --git a/core/java/android/os/health/HealthStatsWriter.java b/core/java/android/os/health/HealthStatsWriter.java
new file mode 100644
index 0000000..351836b
--- /dev/null
+++ b/core/java/android/os/health/HealthStatsWriter.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * Class to write the health stats data into a parcel, so it can then be
+ * retrieved via a {@link HealthStats} object.
+ *
+ * There is an attempt to keep this class as low overhead as possible, for
+ * example storing an int[] and a long[] instead of a TimerStat[].
+ *
+ * @hide
+ */
+public class HealthStatsWriter {
+ private final HealthKeys.Constants mConstants;
+
+ // TimerStat fields
+ private final boolean[] mTimerFields;
+ private final int[] mTimerCounts;
+ private final long[] mTimerTimes;
+
+ // Measurement fields
+ private final boolean[] mMeasurementFields;
+ private final long[] mMeasurementValues;
+
+ // Stats fields
+ private final ArrayMap<String,HealthStatsWriter>[] mStatsValues;
+
+ // Timers fields
+ private final ArrayMap<String,TimerStat>[] mTimersValues;
+
+ // Measurements fields
+ private final ArrayMap<String,Long>[] mMeasurementsValues;
+
+ /**
+ * Construct a HealthStatsWriter object with the given constants.
+ *
+ * The "getDataType()" of the resulting HealthStats object will be the
+ * short name of the java class that the Constants object was initalized
+ * with.
+ */
+ public HealthStatsWriter(HealthKeys.Constants constants) {
+ mConstants = constants;
+
+ // TimerStat
+ final int timerCount = constants.getSize(HealthKeys.TYPE_TIMER);
+ mTimerFields = new boolean[timerCount];
+ mTimerCounts = new int[timerCount];
+ mTimerTimes = new long[timerCount];
+
+ // Measurement
+ final int measurementCount = constants.getSize(HealthKeys.TYPE_MEASUREMENT);
+ mMeasurementFields = new boolean[measurementCount];
+ mMeasurementValues = new long[measurementCount];
+
+ // Stats
+ final int statsCount = constants.getSize(HealthKeys.TYPE_STATS);
+ mStatsValues = new ArrayMap[statsCount];
+
+ // Timers
+ final int timersCount = constants.getSize(HealthKeys.TYPE_TIMERS);
+ mTimersValues = new ArrayMap[timersCount];
+
+ // Measurements
+ final int measurementsCount = constants.getSize(HealthKeys.TYPE_MEASUREMENTS);
+ mMeasurementsValues = new ArrayMap[measurementsCount];
+ }
+
+ /**
+ * Add a timer for the given key.
+ */
+ public void addTimer(int timerId, int count, long time) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_TIMER, timerId);
+
+ mTimerFields[index] = true;
+ mTimerCounts[index] = count;
+ mTimerTimes[index] = time;
+ }
+
+ /**
+ * Add a measurement for the given key.
+ */
+ public void addMeasurement(int measurementId, long value) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENT, measurementId);
+
+ mMeasurementFields[index] = true;
+ mMeasurementValues[index] = value;
+ }
+
+ /**
+ * Add a recursive HealthStats object for the given key and string name. The value
+ * is stored as a HealthStatsWriter until this object is written to a parcel, so
+ * don't attempt to reuse the HealthStatsWriter.
+ *
+ * The value field should not be null.
+ */
+ public void addStats(int key, String name, HealthStatsWriter value) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_STATS, key);
+
+ ArrayMap<String,HealthStatsWriter> map = mStatsValues[index];
+ if (map == null) {
+ map = mStatsValues[index] = new ArrayMap<String,HealthStatsWriter>(1);
+ }
+ map.put(name, value);
+ }
+
+ /**
+ * Add a TimerStat for the given key and string name.
+ *
+ * The value field should not be null.
+ */
+ public void addTimers(int key, String name, TimerStat value) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_TIMERS, key);
+
+ ArrayMap<String,TimerStat> map = mTimersValues[index];
+ if (map == null) {
+ map = mTimersValues[index] = new ArrayMap<String,TimerStat>(1);
+ }
+ map.put(name, value);
+ }
+
+ /**
+ * Add a measurement for the given key and string name.
+ */
+ public void addMeasurements(int key, String name, long value) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENTS, key);
+
+ ArrayMap<String,Long> map = mMeasurementsValues[index];
+ if (map == null) {
+ map = mMeasurementsValues[index] = new ArrayMap<String,Long>(1);
+ }
+ map.put(name, value);
+ }
+
+ /**
+ * Flattens the data in this HealthStatsWriter to the Parcel format
+ * that can be unparceled into a HealthStat.
+ * @more
+ * (Called flattenToParcel because this HealthStatsWriter itself is
+ * not parcelable and we don't flatten all the business about the
+ * HealthKeys.Constants, only the values that were actually supplied)
+ */
+ public void flattenToParcel(Parcel out) {
+ int[] keys;
+
+ // Header fields
+ out.writeString(mConstants.getDataType());
+
+ // TimerStat fields
+ out.writeInt(countBooleanArray(mTimerFields));
+ keys = mConstants.getKeys(HealthKeys.TYPE_TIMER);
+ for (int i=0; i<keys.length; i++) {
+ if (mTimerFields[i]) {
+ out.writeInt(keys[i]);
+ out.writeInt(mTimerCounts[i]);
+ out.writeLong(mTimerTimes[i]);
+ }
+ }
+
+ // Measurement fields
+ out.writeInt(countBooleanArray(mMeasurementFields));
+ keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENT);
+ for (int i=0; i<keys.length; i++) {
+ if (mMeasurementFields[i]) {
+ out.writeInt(keys[i]);
+ out.writeLong(mMeasurementValues[i]);
+ }
+ }
+
+ // Stats
+ out.writeInt(countObjectArray(mStatsValues));
+ keys = mConstants.getKeys(HealthKeys.TYPE_STATS);
+ for (int i=0; i<keys.length; i++) {
+ if (mStatsValues[i] != null) {
+ out.writeInt(keys[i]);
+ writeHealthStatsWriterMap(out, mStatsValues[i]);
+ }
+ }
+
+ // Timers
+ out.writeInt(countObjectArray(mTimersValues));
+ keys = mConstants.getKeys(HealthKeys.TYPE_TIMERS);
+ for (int i=0; i<keys.length; i++) {
+ if (mTimersValues[i] != null) {
+ out.writeInt(keys[i]);
+ writeParcelableMap(out, mTimersValues[i]);
+ }
+ }
+
+ // Measurements
+ out.writeInt(countObjectArray(mMeasurementsValues));
+ keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENTS);
+ for (int i=0; i<keys.length; i++) {
+ if (mMeasurementsValues[i] != null) {
+ out.writeInt(keys[i]);
+ writeLongsMap(out, mMeasurementsValues[i]);
+ }
+ }
+ }
+
+ /**
+ * Count how many of the fields have been set.
+ */
+ private static int countBooleanArray(boolean[] fields) {
+ int count = 0;
+ final int N = fields.length;
+ for (int i=0; i<N; i++) {
+ if (fields[i]) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Count how many of the fields have been set.
+ */
+ private static <T extends Object> int countObjectArray(T[] fields) {
+ int count = 0;
+ final int N = fields.length;
+ for (int i=0; i<N; i++) {
+ if (fields[i] != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Write a map of String to HealthStatsWriter to the Parcel.
+ */
+ private static void writeHealthStatsWriterMap(Parcel out,
+ ArrayMap<String,HealthStatsWriter> map) {
+ final int N = map.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ out.writeString(map.keyAt(i));
+ map.valueAt(i).flattenToParcel(out);
+ }
+ }
+
+ /**
+ * Write a map of String to Parcelables to the Parcel.
+ */
+ private static <T extends Parcelable> void writeParcelableMap(Parcel out,
+ ArrayMap<String,T> map) {
+ final int N = map.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ out.writeString(map.keyAt(i));
+ map.valueAt(i).writeToParcel(out, 0);
+ }
+ }
+
+ /**
+ * Write a map of String to Longs to the Parcel.
+ */
+ private static void writeLongsMap(Parcel out, ArrayMap<String,Long> map) {
+ final int N = map.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ out.writeString(map.keyAt(i));
+ out.writeLong(map.valueAt(i));
+ }
+ }
+}
+
+
diff --git a/core/java/android/os/health/PackageHealthStats.java b/core/java/android/os/health/PackageHealthStats.java
new file mode 100644
index 0000000..2c30d5f
--- /dev/null
+++ b/core/java/android/os/health/PackageHealthStats.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PACKAGES UidHealthStats.STATS_PACKAGES} key.
+ */
+public final class PackageHealthStats {
+
+ private PackageHealthStats() {
+ }
+
+ /**
+ * Key for a HealthStats with {@link ServiceHealthStats} keys for each of the
+ * services defined in this apk.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+ public static final int STATS_SERVICES = HealthKeys.BASE_PACKAGE + 1;
+
+ /**
+ * Key for a map of the number of times that a package's wakeup alarms have fired
+ * while the device was on battery.
+ *
+ * @see android.app.AlarmManager.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENTS)
+ public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = HealthKeys.BASE_PACKAGE + 2;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS
+ = new HealthKeys.Constants(PackageHealthStats.class);
+}
diff --git a/core/java/android/os/health/PidHealthStats.java b/core/java/android/os/health/PidHealthStats.java
new file mode 100644
index 0000000..fe3c02c
--- /dev/null
+++ b/core/java/android/os/health/PidHealthStats.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PIDS UidHealthStats.STATS_PIDS} key.
+ */
+public final class PidHealthStats {
+
+ private PidHealthStats() {
+ }
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WAKE_NESTING_COUNT = HealthKeys.BASE_PID + 1;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WAKE_SUM_MS = HealthKeys.BASE_PID + 2;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WAKE_START_MS = HealthKeys.BASE_PID + 3;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(PidHealthStats.class);
+}
diff --git a/core/java/android/os/health/ProcessHealthStats.java b/core/java/android/os/health/ProcessHealthStats.java
new file mode 100644
index 0000000..e004ecb
--- /dev/null
+++ b/core/java/android/os/health/ProcessHealthStats.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PROCESSES UidHealthStats.STATS_PROCESSES} key.
+ */
+public final class ProcessHealthStats {
+
+ private ProcessHealthStats() {
+ }
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_USER_TIME_MS = HealthKeys.BASE_PROCESS + 1;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_SYSTEM_TIME_MS = HealthKeys.BASE_PROCESS + 2;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_STARTS_COUNT = HealthKeys.BASE_PROCESS + 3;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_CRASHES_COUNT = HealthKeys.BASE_PROCESS + 4;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_ANR_COUNT = HealthKeys.BASE_PROCESS + 5;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_FOREGROUND_MS = HealthKeys.BASE_PROCESS + 6;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(ProcessHealthStats.class);
+}
diff --git a/core/java/android/os/health/ServiceHealthStats.java b/core/java/android/os/health/ServiceHealthStats.java
new file mode 100644
index 0000000..802ad31
--- /dev/null
+++ b/core/java/android/os/health/ServiceHealthStats.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link PackageHealthStats#STATS_SERVICES PackageHealthStats.STATS_SERVICES} key.
+ */
+public final class ServiceHealthStats {
+
+ private ServiceHealthStats() {
+ }
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_START_SERVICE_COUNT = HealthKeys.BASE_SERVICE + 1;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_LAUNCH_COUNT = HealthKeys.BASE_SERVICE + 2;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS
+ = new HealthKeys.Constants(ServiceHealthStats.class);
+}
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
new file mode 100644
index 0000000..520e84e
--- /dev/null
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.app.IBatteryStats;
+
+/**
+ * Provides access to data about how various system resources are used by applications.
+ * @more
+ * <b>Battery Usage</b><br>
+ * The statistics related to power (battery) usage are recorded since the device
+ * was last unplugged. It is expected that applications schedule more work to do
+ * while the device is plugged in (e.g. using {@link android.app.job.JobScheduler
+ * JobScheduler}), and while that can affect charging rates, it is still preferable
+ * to actually draining the battery.
+ */
+public class SystemHealthManager {
+ private final IBatteryStats mBatteryStats;
+
+ /**
+ * Construct a new SystemHealthManager object.
+ * @hide
+ */
+ public SystemHealthManager() {
+ mBatteryStats = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ }
+
+ /**
+ * Obtain a SystemHealthManager object for the supplied context.
+ */
+ public static SystemHealthManager from(Context context) {
+ return (SystemHealthManager)context.getSystemService(Context.SYSTEM_HEALTH_SERVICE);
+ }
+
+ /**
+ * Return a {@link HealthStats} object containing a snapshot of system health
+ * metrics for the given uid (user-id, which in usually corresponds to application).
+ * @more
+ *
+ * An application must hold the {@link android.Manifest.permission#BATTERY_STATS
+ * android.permission.BATTERY_STATS} permission in order to retrieve any HealthStats
+ * other than its own.
+ *
+ * @param uid User ID for a given application.
+ * @return A {@link HealthStats} object containing the metrics for the requested
+ * application. The keys for this HealthStats object will be from the {@link UidHealthStats}
+ * class.
+ * @see Process#myUid()
+ */
+ public HealthStats takeUidSnapshot(int uid) {
+ try {
+ final HealthStatsParceler parceler = mBatteryStats.takeUidSnapshot(uid);
+ return parceler.getHealthStats();
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Return a {@link HealthStats} object containing a snapshot of system health
+ * metrics for the application calling this API. This method is the same as calling
+ * {@code takeUidSnapshot(Process.myUid())}.
+ *
+ * @return A {@link HealthStats} object containing the metrics for this application. The keys
+ * for this HealthStats object will be from the {@link UidHealthStats} class.
+ */
+ public HealthStats takeMyUidSnapshot() {
+ return takeUidSnapshot(Process.myUid());
+ }
+
+ /**
+ * Return a {@link HealthStats} object containing a snapshot of system health
+ * metrics for the given uids (user-id, which in usually corresponds to application).
+ * @more
+ *
+ * An application must hold the {@link android.Manifest.permission#BATTERY_STATS
+ * android.permission.BATTERY_STATS} permission in order to retrieve any HealthStats
+ * other than its own.
+ *
+ * @param uids An array of User IDs to retrieve.
+ * @return An array of {@link HealthStats} objects containing the metrics for each of
+ * the requested uids. The keys for this HealthStats object will be from the
+ * {@link UidHealthStats} class.
+ */
+ public HealthStats[] takeUidSnapshots(int[] uids) {
+ try {
+ final HealthStatsParceler[] parcelers = mBatteryStats.takeUidSnapshots(uids);
+ final HealthStats[] results = new HealthStats[uids.length];
+ final int N = uids.length;
+ for (int i=0; i<N; i++) {
+ results[i] = parcelers[i].getHealthStats();
+ }
+ return results;
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+}
+
diff --git a/core/java/android/os/health/TimerStat.java b/core/java/android/os/health/TimerStat.java
new file mode 100644
index 0000000..fc51b60
--- /dev/null
+++ b/core/java/android/os/health/TimerStat.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A TimerStat object stores a count and a time.
+ *
+ * @more
+ * When possible, the other APIs in this package avoid requiring a TimerStat
+ * object to be constructed, even internally, but the getTimers method on
+ * {@link android.os.health.HealthStats} does require TimerStat objects.
+ */
+public class TimerStat implements Parcelable {
+ private int mCount;
+ private long mTime;
+
+ /**
+ * The CREATOR instance for use by aidl Binder interfaces.
+ */
+ public static final Parcelable.Creator<TimerStat> CREATOR
+ = new Parcelable.Creator<TimerStat>() {
+ public TimerStat createFromParcel(Parcel in) {
+ return new TimerStat(in);
+ }
+
+ public TimerStat[] newArray(int size) {
+ return new TimerStat[size];
+ }
+ };
+
+ /**
+ * Construct an empty TimerStat object with the count and time set to 0.
+ */
+ public TimerStat() {
+ }
+
+ /**
+ * Construct a TimerStat object with the supplied count and time fields.
+ *
+ * @param count The count
+ * @param time The time
+ */
+ public TimerStat(int count, long time) {
+ mCount = count;
+ mTime = time;
+ }
+
+ /**
+ * Construct a TimerStat object reading the values from a {@link android.os.Parcel Parcel}
+ * object.
+ */
+ public TimerStat(Parcel in) {
+ mCount = in.readInt();
+ mTime = in.readLong();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Write this TimerStat object to a parcel.
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCount);
+ out.writeLong(mTime);
+ }
+
+ /**
+ * Set the count for this timer.
+ */
+ public void setCount(int count) {
+ mCount = count;
+ }
+
+ /**
+ * Get the count for this timer.
+ */
+ public int getCount() {
+ return mCount;
+ }
+
+ /**
+ * Set the time for this timer.
+ */
+ public void setTime(long time) {
+ mTime = time;
+ }
+
+ /**
+ * Get the time for this timer.
+ */
+ public long getTime() {
+ return mTime;
+ }
+}
diff --git a/core/java/android/os/health/UidHealthStats.java b/core/java/android/os/health/UidHealthStats.java
new file mode 100644
index 0000000..c7d257f
--- /dev/null
+++ b/core/java/android/os/health/UidHealthStats.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link SystemHealthManager#takeUidSnapshot(int) SystemHealthManager.takeUidSnapshot(int)},
+ * {@link SystemHealthManager#takeMyUidSnapshot() SystemHealthManager.takeMyUidSnapshot()}, and
+ * {@link SystemHealthManager#takeUidSnapshots(int[]) SystemHealthManager.takeUidSnapshots(int[])}.
+ */
+public final class UidHealthStats {
+
+ private UidHealthStats() {
+ }
+
+ /**
+ * How many milliseconds this statistics report covers in wall-clock time while the
+ * device was on battery including both screen-on and screen-off time.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_REALTIME_BATTERY_MS = HealthKeys.BASE_UID + 1;
+
+ /**
+ * How many milliseconds this statistics report covers that the CPU was running while the
+ * device was on battery including both screen-on and screen-off time.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_UPTIME_BATTERY_MS = HealthKeys.BASE_UID + 2;
+
+ /**
+ * How many milliseconds this statistics report covers in wall-clock time while the
+ * device was on battery including both screen-on and screen-off time.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 3;
+
+ /**
+ * How many milliseconds this statistics report covers that the CPU was running while the
+ * device was on battery including both screen-on and screen-off time.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 4;
+
+ /**
+ * Key for a TimerStat for the times a
+ * {@link android.os.PowerManager#FULL_WAKE_LOCK full wake lock}
+ * was acquired for this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_WAKELOCKS_FULL = HealthKeys.BASE_UID + 5;
+
+ /**
+ * Key for a TimerStat for the times a
+ * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK full wake lock}
+ * was acquired for this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_WAKELOCKS_PARTIAL = HealthKeys.BASE_UID + 6;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_WAKELOCKS_WINDOW = HealthKeys.BASE_UID + 7;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_WAKELOCKS_DRAW = HealthKeys.BASE_UID + 8;
+
+ /**
+ * Key for a map of Timers for the sync adapter syncs that were done for
+ * this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_SYNCS = HealthKeys.BASE_UID + 9;
+
+ /**
+ * Key for a map of Timers for the {@link android.app.job.JobScheduler} jobs for
+ * this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_JOBS = HealthKeys.BASE_UID + 10;
+
+ /**
+ * Key for a timer for the applications use of the GPS sensor.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_GPS_SENSOR = HealthKeys.BASE_UID + 11;
+
+ /**
+ * Key for a map of the sensor usage for this uid. The keys are a
+ * string representation of the handle for the sensor.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_SENSORS = HealthKeys.BASE_UID + 12;
+
+ /**
+ * Key for a HealthStats with {@link PidHealthStats} keys for each of the
+ * currently running processes for this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+ public static final int STATS_PIDS = HealthKeys.BASE_UID + 13;
+
+ /**
+ * Key for a HealthStats with {@link ProcessHealthStats} keys for each of the
+ * named processes for this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+ public static final int STATS_PROCESSES = HealthKeys.BASE_UID + 14;
+
+ /**
+ * Key for a HealthStats with {@link PackageHealthStats} keys for each of the
+ * APKs that share this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+ public static final int STATS_PACKAGES = HealthKeys.BASE_UID + 15;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_IDLE_MS = HealthKeys.BASE_UID + 16;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_RX_MS = HealthKeys.BASE_UID + 17;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_TX_MS = HealthKeys.BASE_UID + 18;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_POWER_MAMS = HealthKeys.BASE_UID + 19;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = HealthKeys.BASE_UID + 20;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_RX_MS = HealthKeys.BASE_UID + 21;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_TX_MS = HealthKeys.BASE_UID + 22;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = HealthKeys.BASE_UID + 23;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_IDLE_MS = HealthKeys.BASE_UID + 24;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_RX_MS = HealthKeys.BASE_UID + 25;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_TX_MS = HealthKeys.BASE_UID + 26;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_POWER_MAMS = HealthKeys.BASE_UID + 27;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_RUNNING_MS = HealthKeys.BASE_UID + 28;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = HealthKeys.BASE_UID + 29;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_WIFI_SCAN = HealthKeys.BASE_UID + 30;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_MULTICAST_MS = HealthKeys.BASE_UID + 31;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_AUDIO = HealthKeys.BASE_UID + 32;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_VIDEO = HealthKeys.BASE_UID + 33;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_FLASHLIGHT = HealthKeys.BASE_UID + 34;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_CAMERA = HealthKeys.BASE_UID + 35;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_FOREGROUND_ACTIVITY = HealthKeys.BASE_UID + 36;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_BLUETOOTH_SCAN = HealthKeys.BASE_UID + 37;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_TOP_MS = HealthKeys.BASE_UID + 38;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = HealthKeys.BASE_UID + 39;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = HealthKeys.BASE_UID + 40;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = HealthKeys.BASE_UID + 41;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = HealthKeys.BASE_UID + 42;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_CACHED_MS = HealthKeys.BASE_UID + 43;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_VIBRATOR = HealthKeys.BASE_UID + 44;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 45;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 46;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 47;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_RX_BYTES = HealthKeys.BASE_UID + 48;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_TX_BYTES = HealthKeys.BASE_UID + 49;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_RX_BYTES = HealthKeys.BASE_UID + 50;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_TX_BYTES = HealthKeys.BASE_UID + 51;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = HealthKeys.BASE_UID + 52;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = HealthKeys.BASE_UID + 53;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_RX_PACKETS = HealthKeys.BASE_UID + 54;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_TX_PACKETS = HealthKeys.BASE_UID + 55;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_RX_PACKETS = HealthKeys.BASE_UID + 56;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_TX_PACKETS = HealthKeys.BASE_UID + 57;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = HealthKeys.BASE_UID + 58;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = HealthKeys.BASE_UID + 59;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_MOBILE_RADIO_ACTIVE = HealthKeys.BASE_UID + 61;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_USER_CPU_TIME_US = HealthKeys.BASE_UID + 62;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_SYSTEM_CPU_TIME_US = HealthKeys.BASE_UID + 63;
+
+ /**
+ * An estimate of the number of milliamp-microsends used by this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_CPU_POWER_MAUS = HealthKeys.BASE_UID + 64;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(UidHealthStats.class);
+}
+
diff --git a/core/java/android/os/health/package.html b/core/java/android/os/health/package.html
new file mode 100644
index 0000000..3a46a5b
--- /dev/null
+++ b/core/java/android/os/health/package.html
@@ -0,0 +1,39 @@
+<html>
+<body>
+
+The android.os.health package contains a set of classes to provide data
+to track the system resources of applications.
+<p>
+Applications running in the background are responsible for a significant amount
+of battery usage on a typical android device. There are several things that
+applications can do in order to reduce their impact. For example, by using
+{@link android.app.job.JobScheduler JobScheduler}, an application does not need
+to independently monitor whether the network is available, whether the device is
+plugged in, etc. In addition to being simpler to use, the application's
+services are only started when the required conditions have been met. But even
+when using the appropriate helper APIs, applications still can reduce their
+footprint. This package provides more insight into what is going on behind the
+scenes when an application is running.
+<p>
+Application data is tracked by which user id (uid) is using particular
+resources. A snapshot of an application's measurements can be taken with the
+{@link android.os.health.SystemHealthManager#takeMyUidSnapshot() SystemHealth.takeMyUidSnapshot()}
+method. The {@link android.os.health.HealthStats} object returned contains the
+statistics.
+<p>
+<b>HealthStats</b><br>
+In order to be returned efficiently, the {@link android.os.health.HealthStats}
+class uses a set of int keys to identify the data returned. The
+{@link android.os.health.UidHealthStats}, {@link android.os.health.PidHealthStats},
+{@link android.os.health.PackageHealthStats} , {@link android.os.health.ProcessHealthStats},
+and {@link android.os.health.ServiceHealthStats} classes provide those constants.
+Each {@link android.os.health.HealthStats} object will be associated with
+exactly one of those classes. The object returned from
+{@link android.os.health.SystemHealthManager#takeMyUidSnapshot() SystemHealth.takeMyUidSnapshot()}
+will be using the {@link android.os.health.UidHealthStats} keys, as it contains all
+of the data available for that uid.
+
+
+</body>
+</html>
+
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d43ff4e..d4ff766 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7786,6 +7786,31 @@
public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants";
/**
+ * ShortcutManager specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "reset_interval_sec=86400,max_daily_updates=5"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * reset_interval_sec (long)
+ * max_daily_updates (int)
+ * max_icon_dimension_dp (int, DP)
+ * max_icon_dimension_dp_lowram (int, DP)
+ * max_shortcuts (int)
+ * icon_quality (int, 0-100)
+ * icon_format (String)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.pm.ShortcutService.ConfigConstants
+ */
+ public static final String SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants";
+
+ /**
* Get the key that retrieves a bluetooth headset's priority.
* @hide
*/
diff --git a/core/java/android/service/notification/NotificationRankerService.java b/core/java/android/service/notification/NotificationRankerService.java
index e325354..47fdac6 100644
--- a/core/java/android/service/notification/NotificationRankerService.java
+++ b/core/java/android/service/notification/NotificationRankerService.java
@@ -119,7 +119,13 @@
@Override
public void registerAsSystemService(Context context, ComponentName componentName,
int currentUser) {
- throw new IllegalStateException("the ranker may not start itself.");
+ throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
+ }
+
+ /** @hide */
+ @Override
+ public void unregisterAsSystemService() {
+ throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
}
@Override
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c54ce80..d29bc21 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5357,9 +5357,6 @@
void offsetRectBetweenParentAndChild(View descendant, Rect rect,
boolean offsetFromChildToParent, boolean clipToBounds) {
- final RectF rectF = mAttachInfo != null ? mAttachInfo.mTmpTransformRect1 : new RectF();
- final Matrix inverse = mAttachInfo != null ? mAttachInfo.mTmpMatrix : new Matrix();
-
// already in the same coord system :)
if (descendant == this) {
return;
@@ -5373,16 +5370,8 @@
&& (theParent != this)) {
if (offsetFromChildToParent) {
- rect.offset(-descendant.mScrollX, -descendant.mScrollY);
-
- if (!descendant.hasIdentityMatrix()) {
- rectF.set(rect);
- descendant.getMatrix().mapRect(rectF);
- rectF.roundOut(rect);
- }
-
- rect.offset(descendant.mLeft, descendant.mTop);
-
+ rect.offset(descendant.mLeft - descendant.mScrollX,
+ descendant.mTop - descendant.mScrollY);
if (clipToBounds) {
View p = (View) theParent;
boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
@@ -5400,16 +5389,8 @@
rect.setEmpty();
}
}
- rect.offset(-descendant.mLeft, -descendant.mTop);
-
- if (!descendant.hasIdentityMatrix()) {
- descendant.getMatrix().invert(inverse);
- rectF.set(rect);
- inverse.mapRect(rectF);
- rectF.roundOut(rect);
- }
-
- rect.offset(descendant.mScrollX, descendant.mScrollY);
+ rect.offset(descendant.mScrollX - descendant.mLeft,
+ descendant.mScrollY - descendant.mTop);
}
descendant = (View) theParent;
@@ -5420,26 +5401,11 @@
// to get into our coordinate space
if (theParent == this) {
if (offsetFromChildToParent) {
- rect.offset(-descendant.mScrollX, -descendant.mScrollY);
-
- if (!descendant.hasIdentityMatrix()) {
- rectF.set(rect);
- descendant.getMatrix().mapRect(rectF);
- rectF.roundOut(rect);
- }
-
- rect.offset(descendant.mLeft, descendant.mTop);
+ rect.offset(descendant.mLeft - descendant.mScrollX,
+ descendant.mTop - descendant.mScrollY);
} else {
- rect.offset(-descendant.mLeft, -descendant.mTop);
-
- if (!descendant.hasIdentityMatrix()) {
- descendant.getMatrix().invert(inverse);
- rectF.set(rect);
- inverse.mapRect(rectF);
- rectF.roundOut(rect);
- }
-
- rect.offset(descendant.mScrollX, descendant.mScrollY);
+ rect.offset(descendant.mScrollX - descendant.mLeft,
+ descendant.mScrollY - descendant.mTop);
}
} else {
throw new IllegalArgumentException("parameter must be a descendant of this view");
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 74fe94f..8e38c5a 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -20,6 +20,7 @@
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
+import android.os.health.HealthStatsParceler;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.SignalStrength;
@@ -125,4 +126,7 @@
void noteBleScanStarted(in WorkSource ws);
void noteBleScanStopped(in WorkSource ws);
void noteResetBleScan();
+
+ HealthStatsParceler takeUidSnapshot(int uid);
+ HealthStatsParceler[] takeUidSnapshots(in int[] uid);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 60c9e14..c484121 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5300,6 +5300,12 @@
}
@Override
+ public Timer getProcessStateTimer(int state) {
+ if (state < 0 || state >= NUM_PROCESS_STATE) return null;
+ return mProcessStateTimer[state];
+ }
+
+ @Override
public Timer getVibratorOnTimer() {
return mVibratorOnTimer;
}
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 84d0fc7..9907ea9 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -279,6 +279,26 @@
}
/**
+ * Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left
+ * if {@param increment} is negative and moves right otherwise.
+ */
+ public SnapTarget cycleNonDismissTarget(SnapTarget snapTarget, int increment) {
+ int index = mTargets.indexOf(snapTarget);
+ if (index != -1) {
+ SnapTarget newTarget = mTargets.get((index + mTargets.size() + increment)
+ % mTargets.size());
+ if (newTarget == mDismissStartTarget) {
+ return mLastSplitTarget;
+ } else if (newTarget == mDismissEndTarget) {
+ return mFirstSplitTarget;
+ } else {
+ return newTarget;
+ }
+ }
+ return snapTarget;
+ }
+
+ /**
* Represents a snap target for the divider.
*/
public static class SnapTarget {
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 29c1075..fb9b1e5 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -201,6 +201,26 @@
const unsigned int mSize;
};
+// Necessary for decodes when the native decoder cannot scale to appropriately match the sampleSize
+// (for example, RAW). If the sampleSize divides evenly into the dimension, we require that the
+// scale matches exactly. If sampleSize does not divide evenly, we allow the decoder to choose how
+// best to round.
+static bool needsFineScale(const int fullSize, const int decodedSize, const int sampleSize) {
+ if (fullSize % sampleSize == 0 && fullSize / sampleSize != decodedSize) {
+ return true;
+ } else if ((fullSize / sampleSize + 1) != decodedSize &&
+ (fullSize / sampleSize) != decodedSize) {
+ return true;
+ }
+ return false;
+}
+
+static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize,
+ const int sampleSize) {
+ return needsFineScale(fullSize.width(), decodedSize.width(), sampleSize) ||
+ needsFineScale(fullSize.height(), decodedSize.height(), sampleSize);
+}
+
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
// This function takes ownership of the input stream. Since the SkAndroidCodec
// will take ownership of the stream, we don't necessarily need to take ownership
@@ -250,7 +270,6 @@
}
}
}
- const bool willScale = scale != 1.0f;
// Create the codec.
NinePatchPeeker peeker;
@@ -269,15 +288,28 @@
prefColorType = kN32_SkColorType;
}
- // Determine the output size and return if the client only wants the size.
+ // Determine the output size.
SkISize size = codec->getSampledDimensions(sampleSize);
+
+ int scaledWidth = size.width();
+ int scaledHeight = size.height();
+ bool willScale = false;
+
+ // Apply a fine scaling step if necessary.
+ if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) {
+ willScale = true;
+ scaledWidth = codec->getInfo().width() / sampleSize;
+ scaledHeight = codec->getInfo().height() / sampleSize;
+ }
+
+ // Set the options and return if the client only wants the size.
if (options != NULL) {
jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat());
if (env->ExceptionCheck()) {
return nullObjectReturn("OOM in encodedFormatToString()");
}
- env->SetIntField(options, gOptions_widthFieldID, size.width());
- env->SetIntField(options, gOptions_heightFieldID, size.height());
+ env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
+ env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
env->SetObjectField(options, gOptions_mimeFieldID, mimeType);
if (onlyDecodeSize) {
@@ -285,6 +317,13 @@
}
}
+ // Scale is necessary due to density differences.
+ if (scale != 1.0f) {
+ willScale = true;
+ scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f);
+ scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
+ }
+
android::Bitmap* reuseBitmap = nullptr;
unsigned int existingBufferSize = 0;
if (javaBitmap != NULL) {
@@ -381,13 +420,6 @@
return nullObjectReturn("codec->getAndroidPixels() failed.");
}
- int scaledWidth = size.width();
- int scaledHeight = size.height();
- if (willScale) {
- scaledWidth = int(scaledWidth * scale + 0.5f);
- scaledHeight = int(scaledHeight * scale + 0.5f);
- }
-
jbyteArray ninePatchChunk = NULL;
if (peeker.mPatch != NULL) {
if (willScale) {
diff --git a/core/res/res/values-mcc232-mnc11/config.xml b/core/res/res/values-mcc232-mnc11/config.xml
new file mode 100644
index 0000000..91e37b4
--- /dev/null
+++ b/core/res/res/values-mcc232-mnc11/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23201</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc232-mnc12/config.xml b/core/res/res/values-mcc232-mnc12/config.xml
new file mode 100644
index 0000000..91e37b4
--- /dev/null
+++ b/core/res/res/values-mcc232-mnc12/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23201</item>
+ </string-array>
+</resources>
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index af2a944..bae4ecc 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -81,8 +81,7 @@
protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000; // 5 seconds
protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second
- protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
- protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
+ protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 30 * 1000; // 30 seconds
protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
@@ -970,7 +969,7 @@
protected void verifyInt(Cursor cursor, String columnName, int expected) {
int index = cursor.getColumnIndex(columnName);
int actual = cursor.getInt(index);
- assertEquals(expected, actual);
+ assertEquals(String.format("Expected = %d : Actual = %d", expected, actual), expected, actual);
}
/**
diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index 7019980..78a3aa6 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -23,18 +23,18 @@
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.test.suitebuilder.annotation.LargeTest;
-
+import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.Suppress;
import com.google.mockwebserver.MockResponse;
import java.io.File;
+import java.util.concurrent.TimeoutException;
import java.util.Iterator;
import java.util.Set;
/**
* Integration tests of the DownloadManager API.
*/
-@Suppress // Failing.
public class DownloadManagerFunctionalTest extends DownloadManagerBaseTest {
private static final String TAG = "DownloadManagerFunctionalTest";
private final static String CACHE_DIR =
@@ -79,7 +79,11 @@
request.setTitle(DEFAULT_FILENAME);
long dlRequest = mDownloadManager.enqueue(request);
- waitForDownloadOrTimeout(dlRequest);
+ try {
+ waitForDownloadOrTimeout(dlRequest);
+ } catch (TimeoutException ex) {
+ // it is expected to timeout as download never finishes
+ }
Cursor cursor = getCursor(dlRequest);
try {
@@ -92,7 +96,7 @@
/**
* Test a basic download of a binary file 500k in size.
*/
- @LargeTest
+ @MediumTest
public void testBinaryDownloadToSystemCache() throws Exception {
int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.BINARY);
@@ -105,7 +109,7 @@
/**
* Tests the basic downloading of a text file 300000 bytes in size.
*/
- @LargeTest
+ @MediumTest
public void testTextDownloadToSystemCache() throws Exception {
int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.TEXT);
@@ -114,7 +118,7 @@
verifyDownload(dlRequest, blobData);
mDownloadManager.remove(dlRequest);
}
-
+
/**
* Helper to verify a standard single-file download from the mock server, and clean up after
* verification
@@ -135,9 +139,7 @@
verifyFileSize(pfd, fileSize);
verifyFileContents(pfd, fileData);
- int colIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
- String fileName = cursor.getString(colIndex);
- assertTrue(fileName.startsWith(CACHE_DIR));
+ assertTrue(new File(CACHE_DIR + "/" + DEFAULT_FILENAME).exists());
} finally {
pfd.close();
cursor.close();
@@ -147,7 +149,7 @@
/**
* Tests trying to download to SD card when the file with same name already exists.
*/
- @LargeTest
+ @MediumTest
public void testDownloadToExternal_fileExists() throws Exception {
File existentFile = createFileOnSD(null, 1, DataType.TEXT, null);
byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
@@ -161,7 +163,6 @@
Uri localUri = Uri.fromFile(existentFile);
request.setDestinationUri(localUri);
-
long dlRequest = mDownloadManager.enqueue(request);
// wait for the download to complete
@@ -181,7 +182,7 @@
/**
* Tests trying to download a file to SD card.
*/
- @LargeTest
+ @MediumTest
public void testDownloadToExternal() throws Exception {
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
File downloadedFile = new File(localDownloadDirectory, DEFAULT_FILENAME);
@@ -216,7 +217,7 @@
/**
* Tests trying to download a file to the system partition.
*/
- @LargeTest
+ @MediumTest
public void testDownloadToProhibitedDirectory() throws Exception {
File downloadedFile = new File(PROHIBITED_DIRECTORY, DEFAULT_FILENAME);
try {
@@ -246,7 +247,7 @@
/**
* Tests that we get the correct download ID from the download notification.
*/
- @LargeTest
+ @MediumTest
public void testGetDownloadIdOnNotification() throws Exception {
byte[] blobData = generateData(3000, DataType.TEXT); // file size = 3000 bytes
@@ -264,7 +265,7 @@
/**
* Tests the download failure error after too many redirects (>5).
*/
- @LargeTest
+ @MediumTest
public void testErrorTooManyRedirects() throws Exception {
Uri uri = getServerUri(DEFAULT_FILENAME);
@@ -304,7 +305,7 @@
/**
* Tests that we can remove a download from the download manager.
*/
- @LargeTest
+ @MediumTest
public void testRemoveDownload() throws Exception {
int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.BINARY);
@@ -324,7 +325,7 @@
/**
* Tests that we can set the title of a download.
*/
- @LargeTest
+ @MediumTest
public void testSetTitle() throws Exception {
int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.BINARY);
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index dc302c7..bcac6a1 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -88,242 +88,246 @@
</family>
<!-- fallback fonts -->
- <family variant="elegant">
+ <family lang="und-Arab" variant="elegant">
<font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
<font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Arab" variant="compact">
<font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Ethi">
<font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
<font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
<font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Thai" variant="elegant">
<font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Thai" variant="compact">
<font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Armn">
<font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
</family>
- <family>
+ <!-- TODO: add Geok -->
+ <family lang="und-Geor">
<font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Deva" variant="elegant">
<font weight="400" style="normal">NotoSansDevanagari-Regular.ttf</font>
<font weight="700" style="normal">NotoSansDevanagari-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Deva" variant="compact">
<font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansDevanagariUI-Bold.ttf</font>
</family>
- <!-- Gujarati should come after Devanagari -->
- <family variant="elegant">
+
+ <!-- All scripts of India should come after Devanagari, due to shared
+ danda characters.
+ -->
+ <family lang="und-Gujr" variant="elegant">
<font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Gujr" variant="compact">
<font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
</family>
- <!-- Gurmukhi should come after Devanagari -->
- <family variant="elegant">
+ <family lang="und-Guru" variant="elegant">
<font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGurmukhi-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Guru" variant="compact">
<font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGurmukhiUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Taml" variant="elegant">
<font weight="400" style="normal">NotoSansTamil-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTamil-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Taml" variant="compact">
<font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTamilUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Mlym" variant="elegant">
<font weight="400" style="normal">NotoSansMalayalam-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMalayalam-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Mlym" variant="compact">
<font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMalayalamUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Beng" variant="elegant">
<font weight="400" style="normal">NotoSansBengali-Regular.ttf</font>
<font weight="700" style="normal">NotoSansBengali-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Beng" variant="compact">
<font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansBengaliUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Telu" variant="elegant">
<font weight="400" style="normal">NotoSansTelugu-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTelugu-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Telu" variant="compact">
<font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTeluguUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Knda" variant="elegant">
<font weight="400" style="normal">NotoSansKannada-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKannada-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Knda" variant="compact">
<font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKannadaUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Orya" variant="elegant">
<font weight="400" style="normal">NotoSansOriya-Regular.ttf</font>
<font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Orya" variant="compact">
<font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
</family>
- <family>
+
+ <family lang="und-Sinh">
<font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
<font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Khmr" variant="elegant">
<font weight="400" style="normal">NotoSansKhmer-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKhmer-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Khmr" variant="compact">
<font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Laoo" variant="elegant">
<font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
<font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Laoo" variant="compact">
<font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Mymr" variant="elegant">
<font weight="400" style="normal">NotoSansMyanmar-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmar-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Mymr" variant="compact">
<font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmarUI-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Thaa">
<font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Cham">
<font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
<font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Bali">
<font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Bamu">
<font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Batk">
<font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Bugi">
<font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Buhd">
<font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Cans">
<font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Copt">
<font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Glag">
<font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Hano">
<font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Java">
<font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Kali">
<font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Lepc">
<font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Limb">
<font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Lisu">
<font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Mand">
<font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Mtei">
<font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Talu">
<font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Nkoo">
<font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Olck">
<font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Rjng">
<font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Saur">
<font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Sund">
<font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Sylo">
<font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Syre">
<font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Tagb">
<font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Lana">
<font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Tavt">
<font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Tibt">
<font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Tfng">
<font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Vaii">
<font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Yiii">
<font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
</family>
<family>
@@ -332,6 +336,7 @@
<family lang="zh-Hans">
<font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
</family>
+ <!-- TODO: Add Bopo -->
<family lang="zh-Hant">
<font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
</family>
@@ -351,10 +356,10 @@
Tai Le and Mongolian are intentionally kept last, to make sure they don't override
the East Asian punctuation for Chinese.
-->
- <family>
+ <family lang="und-Tale">
<font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Mong">
<font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
</family>
</familyset>
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 6813c89..05596f0 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -35,6 +35,8 @@
#include <android/configuration.h>
+#include <memory>
+
namespace android {
/**
@@ -1876,9 +1878,30 @@
struct Entry;
struct Package;
struct PackageGroup;
- struct bag_set;
typedef Vector<Type*> TypeList;
+ struct bag_set {
+ size_t numAttrs; // number in array
+ size_t availAttrs; // total space in array
+ uint32_t typeSpecFlags;
+ // Followed by 'numAttr' bag_entry structures.
+ };
+
+ /**
+ * Configuration dependent cached data. This must be cleared when the configuration is
+ * changed (setParameters).
+ */
+ struct TypeCacheEntry {
+ TypeCacheEntry() : cachedBags(NULL) {}
+
+ // Computed attribute bags for this type.
+ bag_set** cachedBags;
+
+ // Pre-filtered list of configurations (per asset path) that match the parameters set on this
+ // ResTable.
+ Vector<std::shared_ptr<Vector<const ResTable_type*>>> filteredConfigs;
+ };
+
status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false);
@@ -1900,6 +1923,13 @@
mutable Mutex mLock;
+ // Mutex that controls access to the list of pre-filtered configurations
+ // to check when looking up entries.
+ // When iterating over a bag, the mLock mutex is locked. While mLock is locked,
+ // we do resource lookups.
+ // Mutex is not reentrant, so we must use a different lock than mLock.
+ mutable Mutex mFilteredConfigLock;
+
status_t mError;
ResTable_config mParams;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index bf6ff11..52fe973 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -25,6 +25,7 @@
#include <string.h>
#include <limits>
+#include <memory>
#include <type_traits>
#include <androidfw/ByteBucketArray.h>
@@ -3193,7 +3194,6 @@
, name(_name)
, id(_id)
, largestTypeId(0)
- , bags(NULL)
, dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
, isSystemAsset(_isSystemAsset)
{ }
@@ -3220,36 +3220,41 @@
}
}
+ /**
+ * Clear all cache related data that depends on parameters/configuration.
+ * This includes the bag caches and filtered types.
+ */
void clearBagCache() {
- if (bags) {
+ for (size_t i = 0; i < typeCacheEntries.size(); i++) {
if (kDebugTableNoisy) {
- printf("bags=%p\n", bags);
+ printf("type=%zu\n", i);
}
- for (size_t i = 0; i < bags->size(); i++) {
+ const TypeList& typeList = types[i];
+ if (!typeList.isEmpty()) {
+ TypeCacheEntry& cacheEntry = typeCacheEntries.editItemAt(i);
+
+ // Reset the filtered configurations.
+ cacheEntry.filteredConfigs.clear();
+
+ bag_set** typeBags = cacheEntry.cachedBags;
if (kDebugTableNoisy) {
- printf("type=%zu\n", i);
+ printf("typeBags=%p\n", typeBags);
}
- const TypeList& typeList = types[i];
- if (!typeList.isEmpty()) {
- bag_set** typeBags = bags->get(i);
+
+ if (typeBags) {
+ const size_t N = typeList[0]->entryCount;
if (kDebugTableNoisy) {
- printf("typeBags=%p\n", typeBags);
+ printf("type->entryCount=%zu\n", N);
}
- if (typeBags) {
- const size_t N = typeList[0]->entryCount;
- if (kDebugTableNoisy) {
- printf("type->entryCount=%zu\n", N);
+ for (size_t j = 0; j < N; j++) {
+ if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) {
+ free(typeBags[j]);
}
- for (size_t j = 0; j < N; j++) {
- if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
- free(typeBags[j]);
- }
- free(typeBags);
}
+ free(typeBags);
+ cacheEntry.cachedBags = NULL;
}
}
- delete bags;
- bags = NULL;
}
}
@@ -3277,9 +3282,11 @@
uint8_t largestTypeId;
- // Computed attribute bags, first indexed by the type and second
- // by the entry in that type.
- ByteBucketArray<bag_set**>* bags;
+ // Cached objects dependent on the parameters/configuration of this ResTable.
+ // Gets cleared whenever the parameters/configuration changes.
+ // These are stored here in a parallel structure because the data in `types` may
+ // be shared by other ResTable's (framework resources are shared this way).
+ ByteBucketArray<TypeCacheEntry> typeCacheEntries;
// The table mapping dynamic references to resolved references for
// this package group.
@@ -3293,14 +3300,6 @@
const bool isSystemAsset;
};
-struct ResTable::bag_set
-{
- size_t numAttrs; // number in array
- size_t availAttrs; // total space in array
- uint32_t typeSpecFlags;
- // Followed by 'numAttr' bag_entry structures.
-};
-
ResTable::Theme::Theme(const ResTable& table)
: mTable(table)
, mTypeSpecFlags(0)
@@ -4192,39 +4191,32 @@
}
// First see if we've already computed this bag...
- if (grp->bags) {
- bag_set** typeSet = grp->bags->get(t);
- if (typeSet) {
- bag_set* set = typeSet[e];
- if (set) {
- if (set != (bag_set*)0xFFFFFFFF) {
- if (outTypeSpecFlags != NULL) {
- *outTypeSpecFlags = set->typeSpecFlags;
- }
- *outBag = (bag_entry*)(set+1);
- if (kDebugTableSuperNoisy) {
- ALOGI("Found existing bag for: 0x%x\n", resID);
- }
- return set->numAttrs;
+ TypeCacheEntry& cacheEntry = grp->typeCacheEntries.editItemAt(t);
+ bag_set** typeSet = cacheEntry.cachedBags;
+ if (typeSet) {
+ bag_set* set = typeSet[e];
+ if (set) {
+ if (set != (bag_set*)0xFFFFFFFF) {
+ if (outTypeSpecFlags != NULL) {
+ *outTypeSpecFlags = set->typeSpecFlags;
}
- ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
- resID);
- return BAD_INDEX;
+ *outBag = (bag_entry*)(set+1);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Found existing bag for: 0x%x\n", resID);
+ }
+ return set->numAttrs;
}
+ ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
+ resID);
+ return BAD_INDEX;
}
}
// Bag not found, we need to compute it!
- if (!grp->bags) {
- grp->bags = new ByteBucketArray<bag_set**>();
- if (!grp->bags) return NO_MEMORY;
- }
-
- bag_set** typeSet = grp->bags->get(t);
if (!typeSet) {
typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
if (!typeSet) return NO_MEMORY;
- grp->bags->set(t, typeSet);
+ cacheEntry.cachedBags = typeSet;
}
// Mark that we are currently working on this one.
@@ -4430,18 +4422,56 @@
void ResTable::setParameters(const ResTable_config* params)
{
- mLock.lock();
+ AutoMutex _lock(mLock);
+ AutoMutex _lock2(mFilteredConfigLock);
+
if (kDebugTableGetEntry) {
ALOGI("Setting parameters: %s\n", params->toString().string());
}
mParams = *params;
- for (size_t i=0; i<mPackageGroups.size(); i++) {
+ for (size_t p = 0; p < mPackageGroups.size(); p++) {
+ PackageGroup* packageGroup = mPackageGroups.editItemAt(p);
if (kDebugTableNoisy) {
- ALOGI("CLEARING BAGS FOR GROUP %zu!", i);
+ ALOGI("CLEARING BAGS FOR GROUP %zu!", p);
}
- mPackageGroups[i]->clearBagCache();
+ packageGroup->clearBagCache();
+
+ // Find which configurations match the set of parameters. This allows for a much
+ // faster lookup in getEntry() if the set of values is narrowed down.
+ for (size_t t = 0; t < packageGroup->types.size(); t++) {
+ if (packageGroup->types[t].isEmpty()) {
+ continue;
+ }
+
+ TypeList& typeList = packageGroup->types.editItemAt(t);
+
+ // Retrieve the cache entry for this type.
+ TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries.editItemAt(t);
+
+ for (size_t ts = 0; ts < typeList.size(); ts++) {
+ Type* type = typeList.editItemAt(ts);
+
+ std::shared_ptr<Vector<const ResTable_type*>> newFilteredConfigs =
+ std::make_shared<Vector<const ResTable_type*>>();
+
+ for (size_t ti = 0; ti < type->configs.size(); ti++) {
+ ResTable_config config;
+ config.copyFromDtoH(type->configs[ti]->config);
+
+ if (config.match(mParams)) {
+ newFilteredConfigs->add(type->configs[ti]);
+ }
+ }
+
+ if (kDebugTableNoisy) {
+ ALOGD("Updating pkg=%zu type=%zu with %zu filtered configs",
+ p, t, newFilteredConfigs->size());
+ }
+
+ cacheEntry.filteredConfigs.add(newFilteredConfigs);
+ }
+ }
}
- mLock.unlock();
}
void ResTable::getParameters(ResTable_config* params) const
@@ -5974,9 +6004,32 @@
specFlags = -1;
}
- const size_t numConfigs = typeSpec->configs.size();
+ const Vector<const ResTable_type*>* candidateConfigs = &typeSpec->configs;
+
+ std::shared_ptr<Vector<const ResTable_type*>> filteredConfigs;
+ if (config && memcmp(&mParams, config, sizeof(mParams)) == 0) {
+ // Grab the lock first so we can safely get the current filtered list.
+ AutoMutex _lock(mFilteredConfigLock);
+
+ // This configuration is equal to the one we have previously cached for,
+ // so use the filtered configs.
+
+ const TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries[typeIndex];
+ if (i < cacheEntry.filteredConfigs.size()) {
+ if (cacheEntry.filteredConfigs[i]) {
+ // Grab a reference to the shared_ptr so it doesn't get destroyed while
+ // going through this list.
+ filteredConfigs = cacheEntry.filteredConfigs[i];
+
+ // Use this filtered list.
+ candidateConfigs = filteredConfigs.get();
+ }
+ }
+ }
+
+ const size_t numConfigs = candidateConfigs->size();
for (size_t c = 0; c < numConfigs; c++) {
- const ResTable_type* const thisType = typeSpec->configs[c];
+ const ResTable_type* const thisType = candidateConfigs->itemAt(c);
if (thisType == NULL) {
continue;
}
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index dcfe91e..7cd7fb5 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -282,4 +282,46 @@
testU16StringToInt(u"0x1ffffffff", 0U, false, true);
}
+TEST(ResTableTest, ShareButDontModifyResTable) {
+ ResTable sharedTable;
+ ASSERT_EQ(NO_ERROR, sharedTable.add(basic_arsc, basic_arsc_len));
+
+ ResTable_config param;
+ memset(¶m, 0, sizeof(param));
+ param.language[0] = 'v';
+ param.language[1] = 's';
+ sharedTable.setParameters(¶m);
+
+ // Check that we get the default value for @integer:number1
+ Res_value val;
+ ssize_t block = sharedTable.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(600), val.data);
+
+ // Create a new table that shares the entries of the shared table.
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false));
+
+ // Set a new configuration on the new table.
+ memset(¶m, 0, sizeof(param));
+ param.language[0] = 's';
+ param.language[1] = 'v';
+ param.country[0] = 'S';
+ param.country[1] = 'E';
+ table.setParameters(¶m);
+
+ // Check that we get a new value in the new table.
+ block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(400), val.data);
+
+ // Check that we still get the old value in the shared table.
+ block = sharedTable.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(600), val.data);
}
+
+} // namespace
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index aaac740..6694dd0 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -46,7 +46,7 @@
namespace integer {
enum {
- number1 = 0x7f040000, // default, sv
+ number1 = 0x7f040000, // default, sv, vs
number2 = 0x7f040001, // default
test3 = 0x7f090000, // default (in feature)
diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h
index 13ab4fa..e497401 100644
--- a/libs/androidfw/tests/data/basic/basic_arsc.h
+++ b/libs/androidfw/tests/data/basic/basic_arsc.h
@@ -1,5 +1,5 @@
unsigned char basic_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x68, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
@@ -16,7 +16,7 @@
0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
- 0xa0, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+ 0x44, 0x07, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
@@ -73,68 +73,81 @@
0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x24, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x24, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x4c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x4c, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x44, 0x00, 0x5c, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x4c, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x76, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x90, 0x01, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x90, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x58, 0x02, 0x00, 0x00,
+ 0x01, 0x02, 0x4c, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x98, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -146,16 +159,17 @@
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7f, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x2c, 0x01, 0x00, 0x00,
0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x7c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x84, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02,
+ 0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02,
+ 0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00
};
-unsigned int basic_arsc_len = 1896;
+unsigned int basic_arsc_len = 2060;
diff --git a/libs/androidfw/tests/data/basic/res/values-vs/values.xml b/libs/androidfw/tests/data/basic/res/values-vs/values.xml
new file mode 100644
index 0000000..4a5a640
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-vs/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <integer name="number1">600</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
index b742d28..a2aa598 100644
--- a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
@@ -1,5 +1,5 @@
unsigned char split_de_fr_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0xe4, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0xf4, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
@@ -10,7 +10,7 @@
0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00,
0x61, 0x00, 0x69, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00,
0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x61, 0x00, 0x69, 0x00, 0x20, 0x00,
- 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x5c, 0x03, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x6c, 0x03, 0x00, 0x00,
0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
@@ -57,30 +57,32 @@
0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_de_fr_arsc_len = 996;
+unsigned int split_de_fr_arsc_len = 1012;
diff --git a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
index e9fb7ea..0cc3915 100644
--- a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
@@ -1,10 +1,10 @@
unsigned char split_hdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x08, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x68, 0x00,
0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
- 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+ 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
@@ -50,19 +50,20 @@
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_hdpi_v4_arsc_len = 776;
+unsigned int split_hdpi_v4_arsc_len = 784;
diff --git a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
index 7835f71..d44ba96 100644
--- a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
@@ -1,10 +1,10 @@
unsigned char split_xhdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x78, 0x00,
0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x20, 0x01, 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x20, 0x01, 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00,
0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00,
0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00,
@@ -50,19 +50,20 @@
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_xhdpi_v4_arsc_len = 780;
+unsigned int split_xhdpi_v4_arsc_len = 788;
diff --git a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
index f805db1..2f3f682 100644
--- a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
@@ -1,10 +1,10 @@
unsigned char split_xxhdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x78, 0x00,
0x78, 0x00, 0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x20, 0x01, 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x20, 0x01, 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00,
0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00,
0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00,
@@ -50,19 +50,20 @@
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_xxhdpi_v4_arsc_len = 780;
+unsigned int split_xxhdpi_v4_arsc_len = 788;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index cc38af2..936c7e8 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -253,6 +253,7 @@
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
+ tests/unit/BakedOpDispatcherTests.cpp \
tests/unit/BakedOpStateTests.cpp \
tests/unit/FrameBuilderTests.cpp \
tests/unit/LeakCheckTests.cpp \
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 98493d7..da5ecca 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -324,7 +324,7 @@
}
}
-void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const ClipBase* clip,
+void BakedOpRenderer::renderGlopImpl(const Rect* dirtyBounds, const ClipBase* clip,
const Glop& glop) {
prepareRender(dirtyBounds, clip);
mRenderState.render(glop, mRenderTarget.orthoMatrix);
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 4b65255..1b4065a 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_BAKED_OP_RENDERER_H
-#define ANDROID_HWUI_BAKED_OP_RENDERER_H
+#pragma once
#include "BakedOpState.h"
#include "Matrix.h"
@@ -41,6 +40,7 @@
*/
class BakedOpRenderer {
public:
+ typedef void (*GlopReceiver)(BakedOpRenderer&, const Rect*, const ClipBase*, const Glop&);
/**
* Position agnostic shadow lighting info. Used with all shadow ops in scene.
*/
@@ -54,8 +54,10 @@
uint8_t spotShadowAlpha;
};
- BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, const LightInfo& lightInfo)
- : mRenderState(renderState)
+ BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque,
+ const LightInfo& lightInfo)
+ : mGlopReceiver(DefaultGlopReceiver)
+ , mRenderState(renderState)
, mCaches(caches)
, mOpaque(opaque)
, mLightInfo(lightInfo) {
@@ -81,7 +83,9 @@
}
void renderFunctor(const FunctorOp& op, const BakedOpState& state);
- void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop);
+ void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop) {
+ mGlopReceiver(*this, dirtyBounds, clip, glop);
+ }
bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; }
void dirtyRenderTarget(const Rect& dirtyRect);
bool didDraw() const { return mHasDrawn; }
@@ -95,7 +99,14 @@
drawRects(ltrb, 4, paint);
}
void drawRects(const float* rects, int count, const SkPaint* paint);
+protected:
+ GlopReceiver mGlopReceiver;
private:
+ static void DefaultGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
+ const ClipBase* clip, const Glop& glop) {
+ renderer.renderGlopImpl(dirtyBounds, clip, glop);
+ }
+ void renderGlopImpl(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop);
void setViewport(uint32_t width, uint32_t height);
void clearColorBuffer(const Rect& clearRect);
void prepareRender(const Rect* dirtyBounds, const ClipBase* clip);
@@ -136,5 +147,3 @@
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_BAKED_OP_RENDERER_H
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index deab956..709156c 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -437,8 +437,16 @@
y = dy * dz;
}
+/**
+ * Set the contents of the rect to be the bounding rect around each of the corners, mapped by the
+ * matrix.
+ *
+ * NOTE: an empty rect to an arbitrary matrix isn't guaranteed to have an empty output, since that's
+ * important for conservative bounds estimation (e.g. rotate45Matrix.mapRect of Rect(0, 10) should
+ * result in non-empty.
+ */
void Matrix4::mapRect(Rect& r) const {
- if (isIdentity() || r.isEmpty()) return;
+ if (isIdentity()) return;
if (isSimple()) {
MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index d9fce9b..de4fa55 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -286,7 +286,8 @@
friend std::ostream& operator<<(std::ostream& os, const Rect& rect) {
if (rect.isEmpty()) {
- return os << "empty";
+ // Print empty, but continue, since empty rects may still have useful coordinate info
+ os << "(empty)";
}
if (rect.left == 0 && rect.top == 0) {
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
new file mode 100644
index 0000000..654ddc6
--- /dev/null
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <RecordedOp.h>
+#include <BakedOpDispatcher.h>
+#include <BakedOpRenderer.h>
+#include <tests/common/TestUtils.h>
+
+using namespace android::uirenderer;
+
+static BakedOpRenderer::LightInfo sLightInfo;
+static Rect sBaseClip(100, 100);
+
+class ValidatingBakedOpRenderer : public BakedOpRenderer {
+public:
+ ValidatingBakedOpRenderer(RenderState& renderState, std::function<void(const Glop& glop)> validator)
+ : BakedOpRenderer(Caches::getInstance(), renderState, true, sLightInfo)
+ , mValidator(validator) {
+ mGlopReceiver = ValidatingGlopReceiver;
+ }
+private:
+ static void ValidatingGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
+ const ClipBase* clip, const Glop& glop) {
+
+ auto vbor = reinterpret_cast<ValidatingBakedOpRenderer*>(&renderer);
+ vbor->mValidator(glop);
+ }
+ std::function<void(const Glop& glop)> mValidator;
+};
+
+typedef void (*BakedOpReceiver)(BakedOpRenderer&, const BakedOpState&);
+
+static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, RecordedOp* op,
+ std::function<void(const Glop& glop)> glopVerifier) {
+ // Create op, and wrap with basic state.
+ LinearAllocator allocator;
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), sBaseClip);
+ auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op);
+ ASSERT_NE(nullptr, state);
+
+ int glopCount = 0;
+ auto glopReceiver = [&glopVerifier, &glopCount] (const Glop& glop) {
+ ASSERT_EQ(glopCount++, 0) << "Only one Glop expected";
+ glopVerifier(glop);
+ };
+ ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
+
+ // Dispatch based on op type created, similar to Frame/LayerBuilder dispatch behavior
+#define X(Type) \
+ [](BakedOpRenderer& renderer, const BakedOpState& state) { \
+ BakedOpDispatcher::on##Type(renderer, static_cast<const Type&>(*(state.op)), state); \
+ },
+ static BakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X);
+#undef X
+ unmergedReceivers[op->opId](renderer, *state);
+ ASSERT_EQ(1, glopCount) << "Exactly one Glop expected";
+}
+
+RENDERTHREAD_TEST(BakedOpDispatcher, onArc_position) {
+ SkPaint strokePaint;
+ strokePaint.setStyle(SkPaint::kStroke_Style);
+ strokePaint.setStrokeWidth(4);
+ ArcOp op(Rect(10, 15, 20, 25), Matrix4::identity(), nullptr, &strokePaint, 0, 270, true);
+ testUnmergedGlopDispatch(renderThread, &op, [] (const Glop& glop) {
+ // validate glop produced by renderPathTexture (so texture, unit quad)
+ auto texture = glop.fill.texture.texture;
+ ASSERT_NE(nullptr, texture);
+ float expectedOffset = floor(4 * 1.5f + 0.5f);
+ EXPECT_EQ(expectedOffset, reinterpret_cast<PathTexture*>(texture)->offset)
+ << "Should see conservative offset from PathCache::computeBounds";
+ Rect expectedBounds(10, 15, 20, 25);
+ expectedBounds.outset(expectedOffset);
+ EXPECT_EQ(expectedBounds, glop.bounds) << "bounds outset by stroke 'offset'";
+ Matrix4 expectedModelView;
+ expectedModelView.loadTranslate(10 - expectedOffset, 15 - expectedOffset, 0);
+ expectedModelView.scale(10 + 2 * expectedOffset, 10 + 2 * expectedOffset, 1);
+ EXPECT_EQ(expectedModelView, glop.transform.modelView)
+ << "X and Y offsets, and scale both applied to model view";
+ });
+}
+
+RENDERTHREAD_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
+ SkPaint layerPaint;
+ layerPaint.setAlpha(128);
+ OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case
+ LayerOp op(Rect(10, 10), Matrix4::identity(), nullptr, &layerPaint, &buffer);
+ testUnmergedGlopDispatch(renderThread, &op, [&renderThread] (const Glop& glop) {
+ // rect glop is dispatched with paint props applied
+ EXPECT_EQ(renderThread.renderState().meshState().getUnitQuadVBO(),
+ glop.mesh.vertices.bufferObject) << "Unit quad should be drawn";
+ EXPECT_EQ(nullptr, glop.fill.texture.texture) << "Should be no texture when layer is null";
+ EXPECT_FLOAT_EQ(128 / 255.0f, glop.fill.color.a) << "Rect quad should use op alpha";
+ });
+}
diff --git a/libs/hwui/tests/unit/MatrixTests.cpp b/libs/hwui/tests/unit/MatrixTests.cpp
index da22637..eddab87 100644
--- a/libs/hwui/tests/unit/MatrixTests.cpp
+++ b/libs/hwui/tests/unit/MatrixTests.cpp
@@ -21,15 +21,30 @@
using namespace android::uirenderer;
-TEST(Matrix, mapRect) {
+TEST(Matrix, mapRect_emptyScaleSkew) {
// Skew, so we don't hit identity/translate/simple fast paths
- Matrix4 matrix;
- matrix.skew(0.1f, 0.1f);
+ Matrix4 scaleMatrix;
+ scaleMatrix.loadScale(10, 10, 1);
+ scaleMatrix.skew(0.1f, 0.1f);
// non-zero empty rect, so sorting x/y would make rect non-empty
- Rect empty(100, 100, -100, -100);
+ Rect empty(15, 20, 15, 100);
ASSERT_TRUE(empty.isEmpty());
- matrix.mapRect(empty);
- EXPECT_TRUE(empty.isEmpty())
- << "Empty rect should always remain empty, regardless of mapping.";
+ scaleMatrix.mapRect(empty);
+ EXPECT_EQ(Rect(170, 215, 250, 1015), empty);
+ EXPECT_FALSE(empty.isEmpty())
+ << "Empty 'line' rect doesn't remain empty when skewed.";
+}
+
+TEST(Matrix, mapRect_emptyRotate) {
+ // Skew, so we don't hit identity/translate/simple fast paths
+ Matrix4 skewMatrix;
+ skewMatrix.loadRotate(45);
+
+ // non-zero empty rect, so sorting x/y would make rect non-empty
+ Rect lineRect(0, 100);
+ ASSERT_TRUE(lineRect.isEmpty());
+ skewMatrix.mapRect(lineRect);
+ EXPECT_FALSE(lineRect.isEmpty())
+ << "Empty 'line' rect doesn't remain empty when rotated.";
}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 3fa078f..ce12e76 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -24,7 +24,6 @@
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
-import android.util.Pair;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
@@ -47,6 +46,7 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.TimeZone;
import java.util.regex.Pattern;
@@ -65,84 +65,267 @@
private static final boolean DEBUG = false;
// The Exif tag names
+ /** Type is String. */
+ public static final String TAG_ARTIST = "Artist";
/** Type is int. */
- public static final String TAG_ORIENTATION = "Orientation";
+ public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+ /** Type is int. */
+ public static final String TAG_COMPRESSION = "Compression";
+ /** Type is String. */
+ public static final String TAG_COPYRIGHT = "Copyright";
/** Type is String. */
public static final String TAG_DATETIME = "DateTime";
/** Type is String. */
+ public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription";
+ /** Type is int. */
+ public static final String TAG_IMAGE_LENGTH = "ImageLength";
+ /** Type is int. */
+ public static final String TAG_IMAGE_WIDTH = "ImageWidth";
+ /** Type is int. */
+ public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+ /** Type is int. */
+ public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
+ /** Type is String. */
public static final String TAG_MAKE = "Make";
/** Type is String. */
public static final String TAG_MODEL = "Model";
/** Type is int. */
- public static final String TAG_FLASH = "Flash";
+ public static final String TAG_ORIENTATION = "Orientation";
/** Type is int. */
- public static final String TAG_IMAGE_WIDTH = "ImageWidth";
+ public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
/** Type is int. */
- public static final String TAG_IMAGE_LENGTH = "ImageLength";
- /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
- public static final String TAG_GPS_LATITUDE = "GPSLatitude";
- /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
- public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
+ public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+ /** Type is rational. */
+ public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+ /** Type is rational. */
+ public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+ /** Type is int. */
+ public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+ /** Type is int. */
+ public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+ /** Type is int. */
+ public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
/** Type is String. */
- public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+ public static final String TAG_SOFTWARE = "Software";
+ /** Type is int. */
+ public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+ /** Type is int. */
+ public static final String TAG_STRIP_OFFSETS = "StripOffsets";
+ /** Type is int. */
+ public static final String TAG_TRANSFER_FUNCTION = "TransferFunction";
+ /** Type is rational. */
+ public static final String TAG_WHITE_POINT = "WhitePoint";
+ /** Type is rational. */
+ public static final String TAG_X_RESOLUTION = "XResolution";
+ /** Type is rational. */
+ public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+ /** Type is int. */
+ public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+ /** Type is int. */
+ public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+ /** Type is rational. */
+ public static final String TAG_Y_RESOLUTION = "YResolution";
+ /** Type is rational. */
+ public static final String TAG_APERTURE_VALUE = "ApertureValue";
+ /** Type is rational. */
+ public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
/** Type is String. */
- public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ public static final String TAG_CFA_PATTERN = "CFAPattern";
+ /** Type is int. */
+ public static final String TAG_COLOR_SPACE = "ColorSpace";
/** Type is String. */
+ public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+ /** Type is rational. */
+ public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+ /** Type is int. */
+ public static final String TAG_CONTRAST = "Contrast";
+ /** Type is int. */
+ public static final String TAG_CUSTOM_RENDERED = "CustomRendered";
+ /** Type is String. */
+ public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ /** Type is String. */
+ public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+ /** Type is String. */
+ public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
+ /** Type is double. */
+ public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+ /** Type is String. */
+ public static final String TAG_EXIF_VERSION = "ExifVersion";
+ /** Type is double. */
+ public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ /** Type is rational. */
+ public static final String TAG_EXPOSURE_INDEX = "ExposureIndex";
+ /** Type is int. */
+ public static final String TAG_EXPOSURE_MODE = "ExposureMode";
+ /** Type is int. */
+ public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
+ /** Type is double. */
public static final String TAG_EXPOSURE_TIME = "ExposureTime";
/** Type is String. */
+ public static final String TAG_F_NUMBER = "FNumber";
+ /** Type is String. */
public static final String TAG_APERTURE = "FNumber";
/** Type is String. */
+ public static final String TAG_FILE_SOURCE = "FileSource";
+ /** Type is int. */
+ public static final String TAG_FLASH = "Flash";
+ /** Type is rational. */
+ public static final String TAG_FLASH_ENERGY = "FlashEnergy";
+ /** Type is String. */
+ public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+ /** Type is rational. */
+ public static final String TAG_FOCAL_LENGTH = "FocalLength";
+ /** Type is int. */
+ public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+ /** Type is int. */
+ public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+ /** Type is rational. */
+ public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+ /** Type is rational. */
+ public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+ /** Type is rational. */
+ public static final String TAG_GAIN_CONTROL = "GainControl";
+ /** Type is String. */
+ public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+ /** Type is String. */
public static final String TAG_ISO = "ISOSpeedRatings";
/** Type is String. */
- public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
+ /** Type is int. */
+ public static final String TAG_LIGHT_SOURCE = "LightSource";
+ /** Type is String. */
+ public static final String TAG_MAKER_NOTE = "MakerNote";
+ /** Type is rational. */
+ public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+ /** Type is int. */
+ public static final String TAG_METERING_MODE = "MeteringMode";
+ /** Type is String. */
+ public static final String TAG_OECF = "OECF";
+ /** Type is int. */
+ public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+ /** Type is int. */
+ public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+ /** Type is String. */
+ public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+ /** Type is int. */
+ public static final String TAG_SATURATION = "Saturation";
+ /** Type is int. */
+ public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+ /** Type is String. */
+ public static final String TAG_SCENE_TYPE = "SceneType";
+ /** Type is int. */
+ public static final String TAG_SENSING_METHOD = "SensingMethod";
+ /** Type is int. */
+ public static final String TAG_SHARPNESS = "Sharpness";
+ /** Type is rational. */
+ public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+ /** Type is String. */
+ public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+ /** Type is String. */
+ public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
/** Type is int. */
public static final String TAG_SUBSEC_TIME = "SubSecTime";
+ /** Type is int. @hide */
+ public static final String TAG_SUBSECTIME = "SubSecTime";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
/** Type is int. */
public static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
/** Type is int. */
- public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
-
- /**
- * @hide
- */
- public static final String TAG_SUBSECTIME = "SubSecTime";
-
+ public static final String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+ /** Type is int. */
+ public static final String TAG_SUBJECT_AREA = "SubjectArea";
+ /** Type is double. */
+ public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+ /** Type is int. */
+ public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+ /** Type is int. */
+ public static final String TAG_SUBJECT_LOCATION = "SubjectLocation";
+ /** Type is String. */
+ public static final String TAG_USER_COMMENT = "UserComment";
+ /** Type is int. */
+ public static final String TAG_WHITE_BALANCE = "WhiteBalance";
/**
* The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
* Type is rational.
*/
public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
-
/**
* 0 if the altitude is above sea level. 1 if the altitude is below sea
* level. Type is int.
*/
public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
-
/** Type is String. */
- public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
+ /** Type is rational. */
+ public static final String TAG_GPS_DOP = "GPSDOP";
/** Type is String. */
public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
- /** Type is int. */
- public static final String TAG_WHITE_BALANCE = "WhiteBalance";
/** Type is rational. */
- public static final String TAG_FOCAL_LENGTH = "FocalLength";
+ public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+ /** Type is String. */
+ public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+ /** Type is rational. */
+ public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+ /** Type is String. */
+ public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+ /** Type is rational. */
+ public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+ /** Type is String. */
+ public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+ /** Type is rational. */
+ public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+ /** Type is String. */
+ public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+ /** Type is int. */
+ public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+ /** Type is rational. */
+ public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+ /** Type is String. */
+ public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
+ /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
+ public static final String TAG_GPS_LATITUDE = "GPSLatitude";
+ /** Type is String. */
+ public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+ /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
+ public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
+ /** Type is String. */
+ public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ /** Type is String. */
+ public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+ /** Type is String. */
+ public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
/** Type is String. Name of GPS processing method used for location finding. */
public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
- /** Type is double. */
- public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
- /** Type is double. */
- public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
- /** Type is double. */
- public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ /** Type is String. */
+ public static final String TAG_GPS_SATELLITES = "GPSSatellites";
+ /** Type is rational. */
+ public static final String TAG_GPS_SPEED = "GPSSpeed";
+ /** Type is String. */
+ public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+ /** Type is String. */
+ public static final String TAG_GPS_STATUS = "GPSStatus";
+ /** Type is String. */
+ public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ /** Type is rational. */
+ public static final String TAG_GPS_TRACK = "GPSTrack";
+ /** Type is String. */
+ public static final String TAG_GPS_TRACK_REF = "GPSTrackRef";
+ /** Type is String. */
+ public static final String TAG_GPS_VERSION_ID = "GPSVersionID";
+ /** Type is String. */
+ public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
/** Type is int. */
- public static final String TAG_LIGHT_SOURCE = "LightSource";
+ public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
/** Type is int. */
- public static final String TAG_METERING_MODE = "MeteringMode";
- /** Type is int. */
- public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
- /** Type is int. */
- public static final String TAG_EXPOSURE_MODE = "ExposureMode";
+ public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+
+ // Private tags used for pointing the other IFD offset. The types of the following tags are int.
+ private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
+ private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
+ private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
// Private tags used for thumbnail information.
private static final String TAG_HAS_THUMBNAIL = "hasThumbnail";
@@ -215,171 +398,171 @@
// Primary image IFD TIFF tags (See JEITA CP-3451 Table 14. page 54).
private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] {
- new ExifTag("ImageWidth", 256),
- new ExifTag("ImageLength", 257),
- new ExifTag("BitsPerSample", 258),
- new ExifTag("Compression", 259),
- new ExifTag("PhotometricInterpretation", 262),
- new ExifTag("ImageDescription", 270),
- new ExifTag("Make", 271),
- new ExifTag("Model", 272),
- new ExifTag("StripOffsets", 273),
- new ExifTag("Orientation", 274),
- new ExifTag("SamplesPerPixel", 277),
- new ExifTag("RowsPerStrip", 278),
- new ExifTag("StripByteCounts", 279),
- new ExifTag("XResolution", 282),
- new ExifTag("YResolution", 283),
- new ExifTag("PlanarConfiguration", 284),
- new ExifTag("ResolutionUnit", 296),
- new ExifTag("TransferFunction", 301),
- new ExifTag("Software", 305),
- new ExifTag("DateTime", 306),
- new ExifTag("Artist", 315),
- new ExifTag("WhitePoint", 318),
- new ExifTag("PrimaryChromaticities", 319),
- new ExifTag("JPEGInterchangeFormat", 513),
- new ExifTag("JPEGInterchangeFormatLength", 514),
- new ExifTag("YCbCrCoefficients", 529),
- new ExifTag("YCbCrSubSampling", 530),
- new ExifTag("YCbCrPositioning", 531),
- new ExifTag("ReferenceBlackWhite", 532),
- new ExifTag("Copyright", 33432),
- new ExifTag("ExifIFDPointer", 34665),
- new ExifTag("GPSInfoIFDPointer", 34853),
+ new ExifTag(TAG_IMAGE_WIDTH, 256),
+ new ExifTag(TAG_IMAGE_LENGTH, 257),
+ new ExifTag(TAG_BITS_PER_SAMPLE, 258),
+ new ExifTag(TAG_COMPRESSION, 259),
+ new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262),
+ new ExifTag(TAG_IMAGE_DESCRIPTION, 270),
+ new ExifTag(TAG_MAKE, 271),
+ new ExifTag(TAG_MODEL, 272),
+ new ExifTag(TAG_STRIP_OFFSETS, 273),
+ new ExifTag(TAG_ORIENTATION, 274),
+ new ExifTag(TAG_SAMPLES_PER_PIXEL, 277),
+ new ExifTag(TAG_ROWS_PER_STRIP, 278),
+ new ExifTag(TAG_STRIP_BYTE_COUNTS, 279),
+ new ExifTag(TAG_X_RESOLUTION, 282),
+ new ExifTag(TAG_Y_RESOLUTION, 283),
+ new ExifTag(TAG_PLANAR_CONFIGURATION, 284),
+ new ExifTag(TAG_RESOLUTION_UNIT, 296),
+ new ExifTag(TAG_TRANSFER_FUNCTION, 301),
+ new ExifTag(TAG_SOFTWARE, 305),
+ new ExifTag(TAG_DATETIME, 306),
+ new ExifTag(TAG_ARTIST, 315),
+ new ExifTag(TAG_WHITE_POINT, 318),
+ new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319),
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513),
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514),
+ new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529),
+ new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530),
+ new ExifTag(TAG_Y_CB_CR_POSITIONING, 531),
+ new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532),
+ new ExifTag(TAG_COPYRIGHT, 33432),
+ new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
+ new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
};
// Primary image IFD Exif Private tags (See JEITA CP-3451 Table 15. page 55).
private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
- new ExifTag("ExposureTime", 33434),
- new ExifTag("FNumber", 33437),
- new ExifTag("ExposureProgram", 34850),
- new ExifTag("SpectralSensitivity", 34852),
- new ExifTag("ISOSpeedRatings", 34855),
- new ExifTag("OECF", 34856),
- new ExifTag("ExifVersion", 36864),
- new ExifTag("DateTimeOriginal", 36867),
- new ExifTag("DateTimeDigitized", 36868),
- new ExifTag("ComponentsConfiguration", 37121),
- new ExifTag("CompressedBitsPerPixel", 37122),
- new ExifTag("ShutterSpeedValue", 37377),
- new ExifTag("ApertureValue", 37378),
- new ExifTag("BrightnessValue", 37379),
- new ExifTag("ExposureBiasValue", 37380),
- new ExifTag("MaxApertureValue", 37381),
- new ExifTag("SubjectDistance", 37382),
- new ExifTag("MeteringMode", 37383),
- new ExifTag("LightSource", 37384),
- new ExifTag("Flash", 37385),
- new ExifTag("FocalLength", 37386),
- new ExifTag("SubjectArea", 37396),
- new ExifTag("MakerNote", 37500),
- new ExifTag("UserComment", 37510),
- new ExifTag("SubSecTime", 37520),
- new ExifTag("SubSecTimeOriginal", 37521),
- new ExifTag("SubSecTimeDigitized", 37522),
- new ExifTag("FlashpixVersion", 40960),
- new ExifTag("ColorSpace", 40961),
- new ExifTag("PixelXDimension", 40962),
- new ExifTag("PixelYDimension", 40963),
- new ExifTag("RelatedSoundFile", 40964),
- new ExifTag("InteroperabilityIFDPointer", 40965),
- new ExifTag("FlashEnergy", 41483),
- new ExifTag("SpatialFrequencyResponse", 41484),
- new ExifTag("FocalPlaneXResolution", 41486),
- new ExifTag("FocalPlaneYResolution", 41487),
- new ExifTag("FocalPlaneResolutionUnit", 41488),
- new ExifTag("SubjectLocation", 41492),
- new ExifTag("ExposureIndex", 41493),
- new ExifTag("SensingMethod", 41495),
- new ExifTag("FileSource", 41728),
- new ExifTag("SceneType", 41729),
- new ExifTag("CFAPattern", 41730),
- new ExifTag("CustomRendered", 41985),
- new ExifTag("ExposureMode", 41986),
- new ExifTag("WhiteBalance", 41987),
- new ExifTag("DigitalZoomRatio", 41988),
- new ExifTag("FocalLengthIn35mmFilm", 41989),
- new ExifTag("SceneCaptureType", 41990),
- new ExifTag("GainControl", 41991),
- new ExifTag("Contrast", 41992),
- new ExifTag("Saturation", 41993),
- new ExifTag("Sharpness", 41994),
- new ExifTag("DeviceSettingDescription", 41995),
- new ExifTag("SubjectDistanceRange", 41996),
- new ExifTag("ImageUniqueID", 42016),
+ new ExifTag(TAG_EXPOSURE_TIME, 33434),
+ new ExifTag(TAG_F_NUMBER, 33437),
+ new ExifTag(TAG_EXPOSURE_PROGRAM, 34850),
+ new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852),
+ new ExifTag(TAG_ISO, 34855),
+ new ExifTag(TAG_OECF, 34856),
+ new ExifTag(TAG_EXIF_VERSION, 36864),
+ new ExifTag(TAG_DATETIME_ORIGINAL, 36867),
+ new ExifTag(TAG_DATETIME_DIGITIZED, 36868),
+ new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121),
+ new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122),
+ new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377),
+ new ExifTag(TAG_APERTURE_VALUE, 37378),
+ new ExifTag(TAG_BRIGHTNESS_VALUE, 37379),
+ new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380),
+ new ExifTag(TAG_MAX_APERTURE_VALUE, 37381),
+ new ExifTag(TAG_SUBJECT_DISTANCE, 37382),
+ new ExifTag(TAG_METERING_MODE, 37383),
+ new ExifTag(TAG_LIGHT_SOURCE, 37384),
+ new ExifTag(TAG_FLASH, 37385),
+ new ExifTag(TAG_FOCAL_LENGTH, 37386),
+ new ExifTag(TAG_SUBJECT_AREA, 37396),
+ new ExifTag(TAG_MAKER_NOTE, 37500),
+ new ExifTag(TAG_USER_COMMENT, 37510),
+ new ExifTag(TAG_SUBSEC_TIME, 37520),
+ new ExifTag(TAG_SUBSEC_TIME_ORIG, 37521),
+ new ExifTag(TAG_SUBSEC_TIME_DIG, 37522),
+ new ExifTag(TAG_FLASHPIX_VERSION, 40960),
+ new ExifTag(TAG_COLOR_SPACE, 40961),
+ new ExifTag(TAG_PIXEL_X_DIMENSION, 40962),
+ new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963),
+ new ExifTag(TAG_RELATED_SOUND_FILE, 40964),
+ new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965),
+ new ExifTag(TAG_FLASH_ENERGY, 41483),
+ new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484),
+ new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486),
+ new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487),
+ new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488),
+ new ExifTag(TAG_SUBJECT_LOCATION, 41492),
+ new ExifTag(TAG_EXPOSURE_INDEX, 41493),
+ new ExifTag(TAG_SENSING_METHOD, 41495),
+ new ExifTag(TAG_FILE_SOURCE, 41728),
+ new ExifTag(TAG_SCENE_TYPE, 41729),
+ new ExifTag(TAG_CFA_PATTERN, 41730),
+ new ExifTag(TAG_CUSTOM_RENDERED, 41985),
+ new ExifTag(TAG_EXPOSURE_MODE, 41986),
+ new ExifTag(TAG_WHITE_BALANCE, 41987),
+ new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988),
+ new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989),
+ new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990),
+ new ExifTag(TAG_GAIN_CONTROL, 41991),
+ new ExifTag(TAG_CONTRAST, 41992),
+ new ExifTag(TAG_SATURATION, 41993),
+ new ExifTag(TAG_SHARPNESS, 41994),
+ new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995),
+ new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996),
+ new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016),
};
// Primary image IFD GPS Info tags (See JEITA CP-3451 Table 16. page 56).
private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] {
- new ExifTag("GPSVersionID", 0),
- new ExifTag("GPSLatitudeRef", 1),
- new ExifTag("GPSLatitude", 2),
- new ExifTag("GPSLongitudeRef", 3),
- new ExifTag("GPSLongitude", 4),
- new ExifTag("GPSAltitudeRef", 5),
- new ExifTag("GPSAltitude", 6),
- new ExifTag("GPSTimeStamp", 7),
- new ExifTag("GPSSatellites", 8),
- new ExifTag("GPSStatus", 9),
- new ExifTag("GPSMeasureMode", 10),
- new ExifTag("GPSDOP", 11),
- new ExifTag("GPSSpeedRef", 12),
- new ExifTag("GPSSpeed", 13),
- new ExifTag("GPSTrackRef", 14),
- new ExifTag("GPSTrack", 15),
- new ExifTag("GPSImgDirectionRef", 16),
- new ExifTag("GPSImgDirection", 17),
- new ExifTag("GPSMapDatum", 18),
- new ExifTag("GPSDestLatitudeRef", 19),
- new ExifTag("GPSDestLatitude", 20),
- new ExifTag("GPSDestLongitudeRef", 21),
- new ExifTag("GPSDestLongitude", 22),
- new ExifTag("GPSDestBearingRef", 23),
- new ExifTag("GPSDestBearing", 24),
- new ExifTag("GPSDestDistanceRef", 25),
- new ExifTag("GPSDestDistance", 26),
- new ExifTag("GPSProcessingMethod", 27),
- new ExifTag("GPSAreaInformation", 28),
- new ExifTag("GPSDateStamp", 29),
- new ExifTag("GPSDifferential", 30),
+ new ExifTag(TAG_GPS_VERSION_ID, 0),
+ new ExifTag(TAG_GPS_LATITUDE_REF, 1),
+ new ExifTag(TAG_GPS_LATITUDE, 2),
+ new ExifTag(TAG_GPS_LONGITUDE_REF, 3),
+ new ExifTag(TAG_GPS_LONGITUDE, 4),
+ new ExifTag(TAG_GPS_ALTITUDE_REF, 5),
+ new ExifTag(TAG_GPS_ALTITUDE, 6),
+ new ExifTag(TAG_GPS_TIMESTAMP, 7),
+ new ExifTag(TAG_GPS_SATELLITES, 8),
+ new ExifTag(TAG_GPS_STATUS, 9),
+ new ExifTag(TAG_GPS_MEASURE_MODE, 10),
+ new ExifTag(TAG_GPS_DOP, 11),
+ new ExifTag(TAG_GPS_SPEED_REF, 12),
+ new ExifTag(TAG_GPS_SPEED, 13),
+ new ExifTag(TAG_GPS_TRACK_REF, 14),
+ new ExifTag(TAG_GPS_TRACK, 15),
+ new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16),
+ new ExifTag(TAG_GPS_IMG_DIRECTION, 17),
+ new ExifTag(TAG_GPS_MAP_DATUM, 18),
+ new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19),
+ new ExifTag(TAG_GPS_DEST_LATITUDE, 20),
+ new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21),
+ new ExifTag(TAG_GPS_DEST_LONGITUDE, 22),
+ new ExifTag(TAG_GPS_DEST_BEARING_REF, 23),
+ new ExifTag(TAG_GPS_DEST_BEARING, 24),
+ new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25),
+ new ExifTag(TAG_GPS_DEST_DISTANCE, 26),
+ new ExifTag(TAG_GPS_PROCESSING_METHOD, 27),
+ new ExifTag(TAG_GPS_AREA_INFORMATION, 28),
+ new ExifTag(TAG_GPS_DATESTAMP, 29),
+ new ExifTag(TAG_GPS_DIFFERENTIAL, 30),
};
// Primary image IFD Interoperability tag (See JEITA CP-3451 Table 17. page 56).
private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] {
- new ExifTag("InteroperabilityIndex", 1),
+ new ExifTag(TAG_INTEROPERABILITY_INDEX, 1),
};
// IFD Thumbnail tags (See JEITA CP-3451 Table 18. page 57).
private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] {
- new ExifTag("ThumbnailImageWidth", 256),
- new ExifTag("ThumbnailImageLength", 257),
- new ExifTag("BitsPerSample", 258),
- new ExifTag("Compression", 259),
- new ExifTag("PhotometricInterpretation", 262),
- new ExifTag("ImageDescription", 270),
- new ExifTag("Make", 271),
- new ExifTag("Model", 272),
- new ExifTag("StripOffsets", 273),
- new ExifTag("Orientation", 274),
- new ExifTag("SamplesPerPixel", 277),
- new ExifTag("RowsPerStrip", 278),
- new ExifTag("StripByteCounts", 279),
- new ExifTag("XResolution", 282),
- new ExifTag("YResolution", 283),
- new ExifTag("PlanarConfiguration", 284),
- new ExifTag("ResolutionUnit", 296),
- new ExifTag("TransferFunction", 301),
- new ExifTag("Software", 305),
- new ExifTag("DateTime", 306),
- new ExifTag("Artist", 315),
- new ExifTag("WhitePoint", 318),
- new ExifTag("PrimaryChromaticities", 319),
- new ExifTag("JPEGInterchangeFormat", 513),
- new ExifTag("JPEGInterchangeFormatLength", 514),
- new ExifTag("YCbCrCoefficients", 529),
- new ExifTag("YCbCrSubSampling", 530),
- new ExifTag("YCbCrPositioning", 531),
- new ExifTag("ReferenceBlackWhite", 532),
- new ExifTag("Copyright", 33432),
- new ExifTag("ExifIFDPointer", 34665),
- new ExifTag("GPSInfoIFDPointer", 34853),
+ new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256),
+ new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257),
+ new ExifTag(TAG_BITS_PER_SAMPLE, 258),
+ new ExifTag(TAG_COMPRESSION, 259),
+ new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262),
+ new ExifTag(TAG_IMAGE_DESCRIPTION, 270),
+ new ExifTag(TAG_MAKE, 271),
+ new ExifTag(TAG_MODEL, 272),
+ new ExifTag(TAG_STRIP_OFFSETS, 273),
+ new ExifTag(TAG_ORIENTATION, 274),
+ new ExifTag(TAG_SAMPLES_PER_PIXEL, 277),
+ new ExifTag(TAG_ROWS_PER_STRIP, 278),
+ new ExifTag(TAG_STRIP_BYTE_COUNTS, 279),
+ new ExifTag(TAG_X_RESOLUTION, 282),
+ new ExifTag(TAG_Y_RESOLUTION, 283),
+ new ExifTag(TAG_PLANAR_CONFIGURATION, 284),
+ new ExifTag(TAG_RESOLUTION_UNIT, 296),
+ new ExifTag(TAG_TRANSFER_FUNCTION, 301),
+ new ExifTag(TAG_SOFTWARE, 305),
+ new ExifTag(TAG_DATETIME, 306),
+ new ExifTag(TAG_ARTIST, 315),
+ new ExifTag(TAG_WHITE_POINT, 318),
+ new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319),
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513),
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514),
+ new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529),
+ new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530),
+ new ExifTag(TAG_Y_CB_CR_POSITIONING, 531),
+ new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532),
+ new ExifTag(TAG_COPYRIGHT, 33432),
+ new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
+ new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
};
// See JEITA CP-3451 Figure 5. page 9.
@@ -398,9 +581,9 @@
};
// List of tags for pointing to the other image file directory offset.
private static final ExifTag[] IFD_POINTER_TAGS = new ExifTag[] {
- new ExifTag("ExifIFDPointer", 34665),
- new ExifTag("GPSInfoPointer", 34853),
- new ExifTag("InteroperabilityIFDPointer", 40965),
+ new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
+ new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
+ new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965),
};
// List of indices of the indicated tag groups according to the IFD_POINTER_TAGS
private static final int[] IFD_POINTER_TAG_HINTS = new int[] {
@@ -408,15 +591,14 @@
};
// Tags for indicating the thumbnail offset and length
private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
- new ExifTag("JPEGInterchangeFormat", 513);
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513);
private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
- new ExifTag("JPEGInterchangeFormatLength", 514);
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514);
// Mappings from tag number to tag name and each item represents one IFD tag group.
private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
- // Mapping from tag name to tag number and the corresponding tag group.
- private static final HashMap<String, Pair<Integer, Integer>> sExifTagMapForWriting =
- new HashMap<>();
+ // Mappings from tag name to tag number and each item represents one IFD tag group.
+ private static final HashMap[] sExifTagMapsForWriting = new HashMap[EXIF_TAGS.length];
// See JPEG File Interchange Format Version 1.02.
// The following values are defined for handling JPEG streams. In this implementation, we are
@@ -457,19 +639,10 @@
// Build up the hash tables to look up Exif tags for reading Exif tags.
for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
sExifTagMapsForReading[hint] = new HashMap();
+ sExifTagMapsForWriting[hint] = new HashMap();
for (ExifTag tag : EXIF_TAGS[hint]) {
sExifTagMapsForReading[hint].put(tag.number, tag.name);
- }
- }
-
- // Build up the hash tables to look up Exif tags for writing Exif tags.
- // There are some tags that have the same tag name in the different group. For that tags,
- // Primary image TIFF IFD and Exif private IFD have a higher priority to map than the other
- // tag groups. For the same tags, it writes one tag in the only one IFD group, which has the
- // higher priority group.
- for (int hint = EXIF_TAGS.length - 1; hint >= 0; --hint) {
- for (ExifTag tag : EXIF_TAGS[hint]) {
- sExifTagMapForWriting.put(tag.name, new Pair<>(tag.number, hint));
+ sExifTagMapsForWriting[hint].put(tag.name, tag.number);
}
}
}
@@ -477,8 +650,8 @@
private final String mFilename;
private final FileDescriptor mSeekableFileDescriptor;
private final AssetManager.AssetInputStream mAssetInputStream;
- private final HashMap<String, String> mAttributes = new HashMap<>();
private boolean mIsRaw;
+ private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
private boolean mHasThumbnail;
// The following values used for indicating a thumbnail position.
private int mThumbnailOffset;
@@ -554,7 +727,15 @@
* @param tag the name of the tag.
*/
public String getAttribute(String tag) {
- return mAttributes.get(tag);
+ // Retrieves all tag groups. The value from primary image tag group has a higher priority
+ // than the value from the thumbnail tag group if there are more than one candidates.
+ for (int i = 0; i < EXIF_TAGS.length; ++i) {
+ Object value = mAttributes[i].get(tag);
+ if (value != null) {
+ return (String) value;
+ }
+ }
+ return null;
}
/**
@@ -566,11 +747,11 @@
* @param defaultValue the value to return if the tag is not available.
*/
public int getAttributeInt(String tag, int defaultValue) {
- String value = mAttributes.get(tag);
+ String value = getAttribute(tag);
if (value == null) return defaultValue;
try {
return Integer.valueOf(value);
- } catch (NumberFormatException ex) {
+ } catch (NumberFormatException e) {
return defaultValue;
}
}
@@ -584,7 +765,7 @@
* @param defaultValue the value to return if the tag is not available.
*/
public double getAttributeDouble(String tag, double defaultValue) {
- String value = mAttributes.get(tag);
+ String value = getAttribute(tag);
if (value == null) return defaultValue;
try {
int index = value.indexOf("/");
@@ -593,7 +774,7 @@
if (denom == 0) return defaultValue;
double num = Double.parseDouble(value.substring(0, index));
return num / denom;
- } catch (NumberFormatException ex) {
+ } catch (NumberFormatException e) {
return defaultValue;
}
}
@@ -605,11 +786,11 @@
* @param value the value of the tag.
*/
public void setAttribute(String tag, String value) {
- if (value == null) {
- mAttributes.remove(tag);
- return;
+ for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
+ if (sExifTagMapsForWriting[i].containsKey(tag)) {
+ mAttributes[i].put(tag, value);
+ }
}
- mAttributes.put(tag, value);
}
/**
@@ -618,6 +799,11 @@
* determine whether the image data format is JPEG or not.
*/
private void loadAttributes(@NonNull InputStream in) throws IOException {
+ // Initialize mAttributes.
+ for (int i = 0; i < EXIF_TAGS.length; ++i) {
+ mAttributes[i] = new HashMap();
+ }
+
// Process RAW input stream
if (mAssetInputStream != null) {
long asset = mAssetInputStream.getNativeAsset();
@@ -664,8 +850,7 @@
// Mark for disabling the save feature.
mIsRaw = true;
- for (Object obj : map.entrySet()) {
- Map.Entry entry = (Map.Entry) obj;
+ for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) {
String attrName = (String) entry.getKey();
switch (attrName) {
@@ -682,7 +867,7 @@
mThumbnailBytes = (byte[]) entry.getValue();
break;
default:
- mAttributes.put(attrName, (String) entry.getValue());
+ setAttribute(attrName, (String) entry.getValue());
break;
}
}
@@ -704,9 +889,11 @@
// Prints out attributes for debugging.
private void printAttributes() {
- Log.d(TAG, "The size of tags: " + mAttributes.size());
- for (Map.Entry<String, String> entry : mAttributes.entrySet()) {
- Log.d(TAG, "tagName: " + entry.getKey() + ", tagValue: " + entry.getValue());
+ for (int i = 0; i < mAttributes.length; ++i) {
+ Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
+ for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
+ Log.d(TAG, "tagName: " + entry.getKey() + ", tagValue: " + entry.getValue());
+ }
}
}
@@ -851,10 +1038,10 @@
* Exif tags are not available.
*/
public boolean getLatLong(float output[]) {
- String latValue = mAttributes.get(TAG_GPS_LATITUDE);
- String latRef = mAttributes.get(TAG_GPS_LATITUDE_REF);
- String lngValue = mAttributes.get(TAG_GPS_LONGITUDE);
- String lngRef = mAttributes.get(TAG_GPS_LONGITUDE_REF);
+ String latValue = getAttribute(TAG_GPS_LATITUDE);
+ String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
+ String lngValue = getAttribute(TAG_GPS_LONGITUDE);
+ String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
try {
@@ -892,7 +1079,7 @@
* @hide
*/
public long getDateTime() {
- String dateTimeString = mAttributes.get(TAG_DATETIME);
+ String dateTimeString = getAttribute(TAG_DATETIME);
if (dateTimeString == null
|| !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
@@ -904,7 +1091,7 @@
if (datetime == null) return -1;
long msecs = datetime.getTime();
- String subSecs = mAttributes.get(TAG_SUBSECTIME);
+ String subSecs = getAttribute(TAG_SUBSECTIME);
if (subSecs != null) {
try {
long sub = Long.valueOf(subSecs);
@@ -917,7 +1104,7 @@
}
}
return msecs;
- } catch (IllegalArgumentException ex) {
+ } catch (IllegalArgumentException e) {
return -1;
}
}
@@ -928,8 +1115,8 @@
* @hide
*/
public long getGpsDateTime() {
- String date = mAttributes.get(TAG_GPS_DATESTAMP);
- String time = mAttributes.get(TAG_GPS_TIMESTAMP);
+ String date = getAttribute(TAG_GPS_DATESTAMP);
+ String time = getAttribute(TAG_GPS_TIMESTAMP);
if (date == null || time == null
|| (!sNonZeroTimePattern.matcher(date).matches()
&& !sNonZeroTimePattern.matcher(time).matches())) return -1;
@@ -941,7 +1128,7 @@
Date datetime = sFormatter.parse(dateTimeString, pos);
if (datetime == null) return -1;
return datetime.getTime();
- } catch (IllegalArgumentException ex) {
+ } catch (IllegalArgumentException e) {
return -1;
}
}
@@ -1049,8 +1236,7 @@
if (dataInputStream.read(bytes) != length) {
throw new IOException("Invalid exif");
}
- mAttributes.put("UserComment",
- new String(bytes, Charset.forName("US-ASCII")));
+ setAttribute("UserComment", new String(bytes, Charset.forName("US-ASCII")));
break;
}
@@ -1068,10 +1254,9 @@
case MARKER_SOF14:
case MARKER_SOF15: {
dataInputStream.skipBytes(1);
- mAttributes.put("ImageLength",
+ setAttribute("ImageLength",
String.valueOf(dataInputStream.readUnsignedShort()));
- mAttributes.put("ImageWidth",
- String.valueOf(dataInputStream.readUnsignedShort()));
+ setAttribute("ImageWidth", String.valueOf(dataInputStream.readUnsignedShort()));
length -= 5;
break;
}
@@ -1208,39 +1393,44 @@
readImageFileDirectory(dataInputStream, IFD_TIFF_HINT);
// Process thumbnail.
- try {
- int jpegInterchangeFormat = Integer.parseInt(
- mAttributes.get(JPEG_INTERCHANGE_FORMAT_TAG.name));
- int jpegInterchangeFormatLength = Integer.parseInt(
- mAttributes.get(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name));
- // The following code limits the size of thumbnail size not to overflow EXIF data area.
- jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat
- + jpegInterchangeFormatLength, exifOffsetFromBeginning + exifBytes.length)
- - jpegInterchangeFormat;
- if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) {
- mHasThumbnail = true;
- mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
- mThumbnailLength = jpegInterchangeFormatLength;
+ String jpegInterchangeFormatString = getAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
+ String jpegInterchangeFormatLengthString =
+ getAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
+ if (jpegInterchangeFormatString != null && jpegInterchangeFormatLengthString != null) {
+ try {
+ int jpegInterchangeFormat = Integer.parseInt(jpegInterchangeFormatString);
+ int jpegInterchangeFormatLength = Integer
+ .parseInt(jpegInterchangeFormatLengthString);
+ // The following code limits the size of thumbnail size not to overflow EXIF data area.
+ jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat
+ + jpegInterchangeFormatLength, exifOffsetFromBeginning + exifBytes.length)
+ - jpegInterchangeFormat;
+ if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) {
+ mHasThumbnail = true;
+ mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
+ mThumbnailLength = jpegInterchangeFormatLength;
- if (mFilename == null && mAssetInputStream == null
- && mSeekableFileDescriptor == null) {
- // Save the thumbnail in memory if the input doesn't support reading again.
- byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength];
- dataInputStream.seek(jpegInterchangeFormat);
- dataInputStream.readFully(thumbnailBytes);
- mThumbnailBytes = thumbnailBytes;
+ if (mFilename == null && mAssetInputStream == null
+ && mSeekableFileDescriptor == null) {
+ // Save the thumbnail in memory if the input doesn't support reading again.
+ byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength];
+ dataInputStream.seek(jpegInterchangeFormat);
+ dataInputStream.readFully(thumbnailBytes);
+ mThumbnailBytes = thumbnailBytes;
- if (DEBUG) {
- Bitmap bitmap = BitmapFactory.decodeByteArray(
- thumbnailBytes, 0, thumbnailBytes.length);
- Log.d(TAG, "Thumbnail offset: " + mThumbnailOffset + ", length: "
- + mThumbnailLength + ", width: " + bitmap.getWidth() + ", height: "
- + bitmap.getHeight());
+ if (DEBUG) {
+ Bitmap bitmap = BitmapFactory.decodeByteArray(
+ thumbnailBytes, 0, thumbnailBytes.length);
+ Log.d(TAG, "Thumbnail offset: " + mThumbnailOffset + ", length: "
+ + mThumbnailLength + ", width: " + bitmap.getWidth()
+ + ", height: "
+ + bitmap.getHeight());
+ }
}
}
+ } catch (NumberFormatException e) {
+ // Ignored the corrupted image.
}
- } catch (NumberFormatException e) {
- // Ignored the corrupted image.
}
// For compatibility, keep data formats as follows.
@@ -1251,7 +1441,7 @@
convertToRational(TAG_FOCAL_LENGTH);
convertToDouble(TAG_DIGITAL_ZOOM_RATIO);
convertToDouble(TAG_EXPOSURE_TIME);
- convertToDouble(TAG_APERTURE);
+ convertToDouble(TAG_F_NUMBER);
convertToDouble(TAG_SUBJECT_DISTANCE);
convertToInt(TAG_ISO);
convertToDouble(TAG_EXPOSURE_BIAS_VALUE);
@@ -1267,29 +1457,29 @@
convertToTimetamp(TAG_GPS_TIMESTAMP);
// The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
- String valueOfDateTimeOriginal = mAttributes.get("DateTimeOriginal");
+ String valueOfDateTimeOriginal = getAttribute("DateTimeOriginal");
if (valueOfDateTimeOriginal != null) {
- mAttributes.put(TAG_DATETIME, valueOfDateTimeOriginal);
+ setAttribute(TAG_DATETIME, valueOfDateTimeOriginal);
}
// Add the default value.
- if (!mAttributes.containsKey(TAG_IMAGE_WIDTH)) {
- mAttributes.put(TAG_IMAGE_WIDTH, "0");
+ if (getAttribute(TAG_IMAGE_WIDTH) == null) {
+ setAttribute(TAG_IMAGE_WIDTH, "0");
}
- if (!mAttributes.containsKey(TAG_IMAGE_LENGTH)) {
- mAttributes.put(TAG_IMAGE_LENGTH, "0");
+ if (getAttribute(TAG_IMAGE_LENGTH) == null) {
+ setAttribute(TAG_IMAGE_LENGTH, "0");
}
- if (!mAttributes.containsKey(TAG_ORIENTATION)) {
- mAttributes.put(TAG_ORIENTATION, "0");
+ if (getAttribute(TAG_ORIENTATION) == null) {
+ setAttribute(TAG_ORIENTATION, "0");
}
- if (!mAttributes.containsKey(TAG_LIGHT_SOURCE)) {
- mAttributes.put(TAG_LIGHT_SOURCE, "0");
+ if (getAttribute(TAG_LIGHT_SOURCE) == null) {
+ setAttribute(TAG_LIGHT_SOURCE, "0");
}
}
// Converts the tag value to timestamp; Otherwise deletes the given tag.
private void convertToTimetamp(String tagName) {
- String entryValue = mAttributes.get(tagName);
+ String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
String[] components = entryValue.split(",");
@@ -1309,16 +1499,16 @@
int value = numerator / denominator;
stringBuilder.append(String.format("%02d", value));
}
- mAttributes.put(tagName, stringBuilder.toString());
+ setAttribute(tagName, stringBuilder.toString());
} else if (dataFormat != IFD_FORMAT_STRING) {
- mAttributes.remove(tagName);
+ setAttribute(tagName, null);
}
}
// Checks the tag value of a given tag formatted in double type; Otherwise try to convert it to
// double type or delete it.
private void convertToDouble(String tagName) {
- String entryValue = mAttributes.get(tagName);
+ String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
switch (dataFormat) {
@@ -1338,21 +1528,21 @@
}
stringBuilder.append((double) numerator / denominator);
}
- mAttributes.put(tagName, stringBuilder.toString());
+ setAttribute(tagName, stringBuilder.toString());
break;
}
case IFD_FORMAT_DOUBLE:
// Keep it as is.
break;
default:
- mAttributes.remove(tagName);
+ setAttribute(tagName, null);
break;
}
}
// Checks the tag value of a given tag formatted in int type; Otherwise deletes the tag value.
private void convertToRational(String tagName) {
- String entryValue = mAttributes.get(tagName);
+ String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
switch (dataFormat) {
@@ -1367,25 +1557,25 @@
double doubleValue = Double.parseDouble(component);
stringBuilder.append((int) (doubleValue * 10000.0)).append("/").append(10000);
}
- mAttributes.put(tagName, stringBuilder.toString());
+ setAttribute(tagName, stringBuilder.toString());
break;
}
case IFD_FORMAT_SRATIONAL:
// Keep it as is.
break;
default:
- mAttributes.remove(tagName);
+ setAttribute(tagName, null);
break;
}
}
// Checks the tag value of a given tag formatted in int type; Otherwise deletes the tag value.
private void convertToInt(String tagName) {
- String entryValue = mAttributes.get(tagName);
+ String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
if (dataFormat != IFD_FORMAT_SLONG) {
- mAttributes.remove(tagName);
+ setAttribute(tagName, null);
}
}
@@ -1474,7 +1664,7 @@
String entryValue = readExifEntryValue(
dataInputStream, dataFormat, numberOfComponents);
if (entryValue != null) {
- mAttributes.put(tagName, entryValue);
+ setAttribute(tagName, entryValue);
}
} else {
StringBuilder entryValueBuilder = new StringBuilder();
@@ -1485,7 +1675,7 @@
entryValueBuilder.append(readExifEntryValue(
dataInputStream, dataFormat, numberOfComponents));
}
- mAttributes.put(tagName, entryValueBuilder.toString());
+ setAttribute(tagName, entryValueBuilder.toString());
}
if (dataInputStream.peek() != nextEntryOffset) {
@@ -1560,8 +1750,6 @@
StringBuilder stringBuilder = new StringBuilder();
while (true) {
int ch = bytes[index];
- if (ch < 0)
- throw new EOFException();
if (ch == 0)
break;
if (ch >= 32)
@@ -1597,49 +1785,46 @@
int[] ifdOffsets = new int[EXIF_TAGS.length];
int[] ifdDataSizes = new int[EXIF_TAGS.length];
- // Maps to store tags per IFD tag group
- HashMap[] ifdTags = new HashMap[EXIF_TAGS.length];
- for (int i = 0; i < EXIF_TAGS.length; ++i) {
- ifdTags[i] = new HashMap();
- }
-
// Remove IFD pointer tags (we'll re-add it later.)
for (ExifTag tag : IFD_POINTER_TAGS) {
- mAttributes.remove(tag.name);
- }
-
- // Assign tags to the corresponding group
- for (Map.Entry<String, String> entry : mAttributes.entrySet()) {
- Pair<Integer, Integer> pair = sExifTagMapForWriting.get(entry.getKey());
- if (pair != null) {
- int tagNumber = pair.first;
- int hint = pair.second;
- ifdTags[hint].put(tagNumber, entry.getValue());
- }
+ setAttribute(tag.name, null);
}
// Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
// offset when there is one or more tags in the thumbnail IFD.
- if (!ifdTags[IFD_INTEROPERABILITY_HINT].isEmpty()) {
- ifdTags[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].number, "0");
+ if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
+ mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, "0");
}
- if (!ifdTags[IFD_EXIF_HINT].isEmpty()) {
- ifdTags[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].number, "0");
+ if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
+ mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name, "0");
}
- if (!ifdTags[IFD_GPS_HINT].isEmpty()) {
- ifdTags[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].number, "0");
+ if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
+ mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name, "0");
}
+ // Remove old thumbnail data
+ setAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name, null);
+ setAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, null);
if (mHasThumbnail) {
- ifdTags[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.number, "0");
- ifdTags[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.number,
+ mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, "0");
+ mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
String.valueOf(mThumbnailLength));
}
+ // Remove null value tags.
+ for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
+ for (Object obj : mAttributes[hint].entrySet().toArray()) {
+ Map.Entry entry = (Map.Entry) obj;
+ if (entry.getValue() == null) {
+ mAttributes[hint].remove(entry.getKey());
+ }
+ }
+ }
+
// Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
// value which has a bigger size than 4 bytes.
for (int i = 0; i < 5; ++i) {
int sum = 0;
- for (Object entry : ifdTags[i].entrySet()) {
+ for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
String entryValue = (String) ((Map.Entry) entry).getValue();
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
int size = getSizeOfExifEntryValue(dataFormat, entryValue);
@@ -1653,16 +1838,16 @@
// Calculate IFD offsets.
int position = 8;
for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
- if (!ifdTags[hint].isEmpty()) {
+ if (!mAttributes[hint].isEmpty()) {
ifdOffsets[hint] = position;
- position += 2 + ifdTags[hint].size() * 12 + 4 + ifdDataSizes[hint];
+ position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint];
}
}
if (mHasThumbnail) {
int thumbnailOffset = position;
- ifdTags[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.number,
+ mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
String.valueOf(thumbnailOffset));
- ifdTags[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.number,
+ mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
String.valueOf(mThumbnailLength));
mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
position += mThumbnailLength;
@@ -1674,21 +1859,21 @@
Log.d(TAG, "totalSize length: " + totalSize);
for (int i = 0; i < 5; ++i) {
Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
- i, ifdOffsets[i], ifdTags[i].size(), ifdDataSizes[i]));
+ i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
}
}
// Update IFD pointer tags with the calculated offsets.
- if (!ifdTags[IFD_EXIF_HINT].isEmpty()) {
- ifdTags[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].number,
+ if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
+ mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name,
String.valueOf(ifdOffsets[IFD_EXIF_HINT]));
}
- if (!ifdTags[IFD_GPS_HINT].isEmpty()) {
- ifdTags[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].number,
+ if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
+ mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
String.valueOf(ifdOffsets[IFD_GPS_HINT]));
}
- if (!ifdTags[IFD_INTEROPERABILITY_HINT].isEmpty()) {
- ifdTags[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].number,
+ if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
+ mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name,
String.valueOf(ifdOffsets[IFD_INTEROPERABILITY_HINT]));
}
@@ -1701,16 +1886,16 @@
// Write IFD groups. See JEITA CP-3451C Figure 7. page 12.
for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
- if (!ifdTags[hint].isEmpty()) {
+ if (!mAttributes[hint].isEmpty()) {
// See JEITA CP-3451C 4.6.2 IFD structure. page 13.
// Write entry count
- dataOutputStream.writeUnsignedShort(ifdTags[hint].size());
+ dataOutputStream.writeUnsignedShort(mAttributes[hint].size());
// Write entry info
- int dataOffset = ifdOffsets[hint] + 2 + ifdTags[hint].size() * 12 + 4;
- for (Object obj : ifdTags[hint].entrySet()) {
- Map.Entry entry = (Map.Entry) obj;
- int tagNumber = (int) entry.getKey();
+ int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4;
+ for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
+ // Convert tag name to tag number.
+ int tagNumber = (int) sExifTagMapsForWriting[hint].get(entry.getKey());
String entryValue = (String) entry.getValue();
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
@@ -1738,15 +1923,14 @@
// Write the next offset. It writes the offset of thumbnail IFD if there is one or
// more tags in the thumbnail IFD when the current IFD is the primary image TIFF
// IFD; Otherwise 0.
- if (hint == 0 && !ifdTags[IFD_THUMBNAIL_HINT].isEmpty()) {
+ if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) {
dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]);
} else {
dataOutputStream.writeUnsignedInt(0);
}
// Write values of data field exceeding 4 bytes after the next offset.
- for (Object obj : ifdTags[hint].entrySet()) {
- Map.Entry entry = (Map.Entry) obj;
+ for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
String entryValue = (String) entry.getValue();
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
diff --git a/media/tests/MediaFrameworkTest/assets/volantis.jpg b/media/tests/MediaFrameworkTest/assets/volantis.jpg
new file mode 100644
index 0000000..cfe300f
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/assets/volantis.jpg
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/res/raw/volantis.jpg b/media/tests/MediaFrameworkTest/res/raw/volantis.jpg
new file mode 100644
index 0000000..cfe300f
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/res/raw/volantis.jpg
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/res/values/exifinterface.xml b/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
index eb13ff37..d556ad3 100644
--- a/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
+++ b/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
@@ -105,4 +105,34 @@
<item>1</item>
<item />
</array>
+ <array name="volantis_jpg">
+ <item>false</item>
+ <item>0</item>
+ <item>0</item>
+ <item>true</item>
+ <item>37.423</item>
+ <item>-122.162</item>
+ <item>0.0</item>
+ <item>htc</item>
+ <item>Nexus 9</item>
+ <item>1.2904</item>
+ <item>2016:03:09 17:36:42</item>
+ <item>0.0083</item>
+ <item>64</item>
+ <item>3097/1000</item>
+ <item />
+ <item />
+ <item>2016:03:09</item>
+ <item>37/1,25/1,2291/100</item>
+ <item>N</item>
+ <item>122/1,9/1,4330/100</item>
+ <item>W</item>
+ <item />
+ <item>08:35:34</item>
+ <item>720</item>
+ <item>1280</item>
+ <item>175</item>
+ <item>1</item>
+ <item>0</item>
+ </array>
</resources>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
index cff18cf..5bd6079 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
@@ -51,10 +51,12 @@
private static final String EXIF_BYTE_ORDER_II_JPEG = "image_exif_byte_order_ii.jpg";
private static final String EXIF_BYTE_ORDER_MM_JPEG = "image_exif_byte_order_mm.jpg";
private static final String LG_G4_ISO_800_DNG = "lg_g4_iso_800.dng";
+ private static final String VOLANTIS_JPEG = "volantis.jpg";
private static final int[] IMAGE_RESOURCES = new int[] {
- R.raw.image_exif_byte_order_ii, R.raw.image_exif_byte_order_mm, R.raw.lg_g4_iso_800 };
+ R.raw.image_exif_byte_order_ii, R.raw.image_exif_byte_order_mm, R.raw.lg_g4_iso_800,
+ R.raw.volantis };
private static final String[] IMAGE_FILENAMES = new String[] {
- EXIF_BYTE_ORDER_II_JPEG, EXIF_BYTE_ORDER_MM_JPEG, LG_G4_ISO_800_DNG };
+ EXIF_BYTE_ORDER_II_JPEG, EXIF_BYTE_ORDER_MM_JPEG, LG_G4_ISO_800_DNG, VOLANTIS_JPEG };
private static final String[] EXIF_TAGS = {
ExifInterface.TAG_MAKE,
@@ -415,7 +417,7 @@
testExifInterfaceForRaw(LG_G4_ISO_800_DNG, R.array.lg_g4_iso_800_dng);
}
- public void testCorruptedImage() {
+ public void testCorruptedImage() throws Throwable {
byte[] bytes = new byte[1024];
try {
new ExifInterface(new ByteArrayInputStream(bytes));
@@ -424,4 +426,9 @@
// Success
}
}
+
+ public void testReadExifDataFromVolantisJpg() throws Throwable {
+ // Test if it is possible to parse the volantis generated JPEG smoothly.
+ testExifInterfaceForJpeg(VOLANTIS_JPEG, R.array.volantis_jpg);
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index a77a9b3..8fcd9d1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -113,8 +113,8 @@
}
private int collectViewableUris(ArrayList<Uri> uris) {
- final List<String> siblingIds = mModel.getModelIds();
- uris.ensureCapacity(siblingIds.size());
+ final String[] siblingIds = mModel.getModelIds();
+ uris.ensureCapacity(siblingIds.length);
int documentLocation = 0;
Cursor cursor;
@@ -124,8 +124,8 @@
Uri uri;
// Cursor's are not guaranteed to be immutable. Hence, traverse it only once.
- for (int i = 0; i < siblingIds.size(); i++) {
- cursor = mModel.getItem(siblingIds.get(i));
+ for (int i = 0; i < siblingIds.length; i++) {
+ cursor = mModel.getItem(siblingIds[i]);
mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
index 8170e2a..ab4f5c4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
@@ -59,7 +59,7 @@
* A sorted array of model IDs for the files currently in the Model. Sort order is determined
* by {@link #mSortOrder}
*/
- private List<String> mIds = new ArrayList<>();
+ private String mIds[] = new String[0];
private int mSortOrder = SORT_ORDER_DISPLAY_NAME;
@Nullable String info;
@@ -108,7 +108,7 @@
if (result == null) {
mCursor = null;
mCursorCount = 0;
- mIds.clear();
+ mIds = new String[0];
mPositions.clear();
info = null;
error = null;
@@ -152,7 +152,7 @@
*/
private void updateModelData() {
int[] positions = new int[mCursorCount];
- mIds.clear();
+ mIds = new String[mCursorCount];
String[] stringValues = new String[mCursorCount];
long[] longValues = null;
@@ -164,7 +164,7 @@
for (int pos = 0; pos < mCursorCount; ++pos) {
mCursor.moveToNext();
positions[pos] = pos;
- mIds.add(createModelId(mCursor));
+ mIds[pos] = createModelId(mCursor);
switch(mSortOrder) {
case SORT_ORDER_DISPLAY_NAME:
@@ -201,7 +201,7 @@
// Populate the positions.
mPositions.clear();
for (int i = 0; i < mCursorCount; ++i) {
- mPositions.put(mIds.get(i), positions[i]);
+ mPositions.put(mIds[i], positions[i]);
}
}
@@ -214,12 +214,12 @@
* @param positions Cursor positions to be sorted.
* @param ids Model IDs to be sorted.
*/
- private static void binarySort(String[] sortKey, int[] positions, List<String> ids) {
+ private static void binarySort(String[] sortKey, int[] positions, String[] ids) {
final int count = positions.length;
for (int start = 1; start < count; start++) {
final int pivotPosition = positions[start];
final String pivotValue = sortKey[start];
- final String pivotId = ids.get(start);
+ final String pivotId = ids[start];
int left = 0;
int right = start;
@@ -243,23 +243,21 @@
case 2:
positions[left + 2] = positions[left + 1];
sortKey[left + 2] = sortKey[left + 1];
- ids.set(left + 2, ids.get(left + 1));
+ ids[left + 2] = ids[left + 1];
case 1:
positions[left + 1] = positions[left];
sortKey[left + 1] = sortKey[left];
- ids.set(left + 1, ids.get(left));
+ ids[left + 1] = ids[left];
break;
default:
System.arraycopy(positions, left, positions, left + 1, n);
System.arraycopy(sortKey, left, sortKey, left + 1, n);
- for (int i = n; i >= 1; --i) {
- ids.set(left + i, ids.get(left + i - 1));
- }
+ System.arraycopy(ids, left, ids, left + 1, n);
}
positions[left] = pivotPosition;
sortKey[left] = pivotValue;
- ids.set(left, pivotId);
+ ids[left] = pivotId;
}
}
@@ -275,13 +273,13 @@
* @param ids Model IDs to be sorted.
*/
private static void binarySort(
- long[] sortKey, String[] mimeTypes, int[] positions, List<String> ids) {
+ long[] sortKey, String[] mimeTypes, int[] positions, String[] ids) {
final int count = positions.length;
for (int start = 1; start < count; start++) {
final int pivotPosition = positions[start];
final long pivotValue = sortKey[start];
final String pivotMime = mimeTypes[start];
- final String pivotId = ids.get(start);
+ final String pivotId = ids[start];
int left = 0;
int right = start;
@@ -310,7 +308,7 @@
// have identical numerical sort keys. One common example of this scenario is seen
// when sorting a set of active downloads by mod time.
if (compare == 0) {
- compare = pivotId.compareTo(ids.get(mid));
+ compare = pivotId.compareTo(ids[mid]);
}
if (compare < 0) {
@@ -326,26 +324,24 @@
positions[left + 2] = positions[left + 1];
sortKey[left + 2] = sortKey[left + 1];
mimeTypes[left + 2] = mimeTypes[left + 1];
- ids.set(left + 2, ids.get(left + 1));
+ ids[left + 2] = ids[left + 1];
case 1:
positions[left + 1] = positions[left];
sortKey[left + 1] = sortKey[left];
mimeTypes[left + 1] = mimeTypes[left];
- ids.set(left + 1, ids.get(left));
+ ids[left + 1] = ids[left];
break;
default:
System.arraycopy(positions, left, positions, left + 1, n);
System.arraycopy(sortKey, left, sortKey, left + 1, n);
System.arraycopy(mimeTypes, left, mimeTypes, left + 1, n);
- for (int i = n; i >= 1; --i) {
- ids.set(left + i, ids.get(left + i - 1));
- }
+ System.arraycopy(ids, left, ids, left + 1, n);
}
positions[left] = pivotPosition;
sortKey[left] = pivotValue;
mimeTypes[left] = pivotMime;
- ids.set(left, pivotId);
+ ids[left] = pivotId;
}
}
@@ -413,7 +409,7 @@
* @return An ordered array of model IDs representing the documents in the model. It is sorted
* according to the current sort order, which was set by the last model update.
*/
- public List<String> getModelIds() {
+ public String[] getModelIds() {
return mIds;
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
index a8b762b..149ecdd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
@@ -140,8 +140,8 @@
Log.d(TAG, "Updating model with hidden ids: " + mHiddenIds);
}
- List<String> modelIds = model.getModelIds();
- mModelIds = new ArrayList<>(modelIds.size());
+ String[] modelIds = model.getModelIds();
+ mModelIds = new ArrayList<>(modelIds.length);
for (String id : modelIds) {
if (!mHiddenIds.contains(id)) {
mModelIds.add(id);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java
index adc8141..7ab51ba2 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java
@@ -68,8 +68,8 @@
// Tests that the item count is correct.
public void testHide_ItemCount() {
- List<String> ids = mModel.getModelIds();
- mAdapter.hide(ids.get(0), ids.get(1));
+ String[] ids = mModel.getModelIds();
+ mAdapter.hide(ids[0], ids[1]);
assertEquals(mModel.getItemCount() - 2, mAdapter.getItemCount());
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
index 4b0bc41..c6ad511 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
@@ -107,7 +107,7 @@
assertTrue(model.isEmpty());
assertEquals(0, model.getItemCount());
- assertEquals(0, model.getModelIds().size());
+ assertEquals(0, model.getModelIds().length);
}
// Tests that the item count is correct.
@@ -165,10 +165,10 @@
// Tests the base case for Model.getItem.
public void testGetItem() {
- List<String> ids = model.getModelIds();
- assertEquals(ITEM_COUNT, ids.size());
+ String[] ids = model.getModelIds();
+ assertEquals(ITEM_COUNT, ids.length);
for (int i = 0; i < ITEM_COUNT; ++i) {
- Cursor c = model.getItem(ids.get(i));
+ Cursor c = model.getItem(ids[i]);
assertEquals(i, c.getPosition());
}
}
@@ -292,14 +292,14 @@
r.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
model.update(r);
- List<String> ids = model.getModelIds();
+ String[] ids = model.getModelIds();
// Check that all items were accounted for
- assertEquals(ITEM_COUNT + DL_COUNT, ids.size());
+ assertEquals(ITEM_COUNT + DL_COUNT, ids.length);
// Check that active downloads are sorted to the top.
for (int i = 0; i < DL_COUNT; i++) {
- assertTrue(currentDownloads.contains(ids.get(i)));
+ assertTrue(currentDownloads.contains(ids[i]));
}
}
@@ -316,11 +316,11 @@
}
private Selection positionToSelection(int... positions) {
- List<String> ids = model.getModelIds();
+ String[] ids = model.getModelIds();
Selection s = new Selection();
// Construct a selection of the given positions.
for (int p: positions) {
- s.add(ids.get(p));
+ s.add(ids[p]);
}
return s;
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ef397f6..1b49eed 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -775,7 +775,7 @@
<!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
<string name="enabled_by_admin">Enabled by administrator</string>
- <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
+ <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
<string name="disabled_by_admin">Disabled by administrator</string>
<!-- Option in navigation drawer that leads to Settings main screen [CHAR LIMIT=30] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index d0c249f..1f1a9b8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -651,8 +651,11 @@
final int disabledColor = context.getColor(R.color.disabled_text_color);
sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- final ImageSpan image = new RestrictedLockImageSpan(context);
- sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ textView.setCompoundDrawables(null, null, getRestrictedPadlock(context), null);
+ textView.setCompoundDrawablePadding(context.getResources().getDimensionPixelSize(
+ R.dimen.restricted_icon_padding));
+ } else {
+ textView.setCompoundDrawables(null, null, null, null);
}
textView.setText(sb);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 9268994..fcff305 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -145,11 +145,6 @@
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
enabledServicesBuilder.toString(), userId);
-
- // Update accessibility enabled.
- Settings.Secure.putIntForUser(context.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0,
- userId);
}
private static Set<ComponentName> getInstalledServices(Context context) {
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 26a81c8..585984c 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -22,6 +22,9 @@
<!-- Standard notification gravity -->
<integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
+ <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
+ <dimen name="recents_initial_bottom_peek_size">@dimen/recents_task_bar_height</dimen>
+
<dimen name="docked_divider_handle_width">2dp</dimen>
<dimen name="docked_divider_handle_height">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 122413d..a2fa3b9 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -99,6 +99,9 @@
<!-- The top padding for the task stack. -->
<dimen name="recents_stack_top_padding">40dp</dimen>
+ <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
+ <dimen name="recents_initial_bottom_peek_size">100dp</dimen>
+
<!-- The side padding for the task stack. -->
<dimen name="recents_stack_left_right_padding">64dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3d5c606..a7546a9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -295,6 +295,9 @@
<!-- The size of the initial peek area at the top of the stack (below the status bar). -->
<dimen name="recents_initial_top_peek_size">8dp</dimen>
+ <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
+ <dimen name="recents_initial_bottom_peek_size">100dp</dimen>
+
<!-- The size of the peek area at the top of the stack (below the status bar). -->
<dimen name="recents_layout_focused_top_peek_size">@dimen/recents_history_button_height</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1f239c3..7838fea 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -523,6 +523,10 @@
<string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned off.</string>
<!-- Announcement made when the work mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_changed_on">Work mode turned on.</string>
+ <!-- Announcement made when the Data Saver changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_data_saver_changed_off">Data Saver turned off.</string>
+ <!-- Announcement made when the Data Saver changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_data_saver_changed_on">Data Saver turned on.</string>
<!-- Content description of the display brightness slider (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_brightness">Display brightness</string>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index c7fb0ec..b35038d 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -17,26 +17,25 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Picture-in-Picture menu -->
+ <!-- Picture-in-Picture (PIP) menu -->
<eat-comment />
- <!-- Button to close PIP on PIP UI -->
- <string name="pip_close" translatable="false">Close PIP</string>
- <!-- Button to move PIP screen to the fullscreen on PIP UI -->
- <string name="pip_fullscreen" translatable="false">Full screen</string>
- <!-- Button to play the current media on PIP UI -->
- <string name="pip_play" translatable="false">Play</string>
- <!-- Button to pause the current media on PIP UI -->
- <string name="pip_pause" translatable="false">Pause</string>
- <!-- Button to close PIP overlay menu on PIP UI -->
- <string name="pip_cancel" translatable="false">Cancel</string>
- <!-- Overlay text on PIP -->
- <string name="pip_hold_home" translatable="false">Hold <b>HOME</b> to control PIP</string>
- <!-- Picture-in-Picture onboarding screen -->
+ <!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=16] -->
+ <string name="pip_close">Close PIP</string>
+ <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=16] -->
+ <string name="pip_fullscreen">Full screen</string>
+ <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=16] -->
+ <string name="pip_play">Play</string>
+ <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=16] -->
+ <string name="pip_pause">Pause</string>
+ <!-- Overlay text on picture-in-picture (PIP) to indicate that longpress HOME key to control PIP [CHAR LIMIT=25] -->
+ <string name="pip_hold_home">Hold <b>HOME</b> to control PIP</string>
+ <!-- Picture-in-Picture (PIP) onboarding screen -->
<eat-comment />
- <!-- Description for onboarding screen. -->
- <string name="pip_onboarding_description" translatable="false">Press and hold the HOME\nbutton to close or control it</string>
- <!-- Button to close onboarding screen. -->
- <string name="pip_onboarding_button" translatable="false">Got it</string>
+ <!-- Description for picture-in-picture (PIP) onboarding screen to indicate that longpress HOME key to control PIP.
+ Line break is needed as if we have CHAR LIMIT=25. [CHAR LIMIT=NONE] -->
+ <string name="pip_onboarding_description">Press and hold the HOME\nbutton to control PIP</string>
+ <!-- Button to close picture-in-picture (PIP) onboarding screen. -->
+ <string name="pip_onboarding_button">Got it</string>
<!-- Font for Recents -->
<!-- DO NOT TRANSLATE -->
<string name="font_roboto_regular" translatable="false">sans-serif</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index fa235d3..74b3fdc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -73,6 +73,15 @@
}
@Override
+ protected String composeChangeAnnouncement() {
+ if (mState.value) {
+ return mContext.getString(R.string.accessibility_quick_settings_data_saver_changed_on);
+ } else {
+ return mContext.getString(R.string.accessibility_quick_settings_data_saver_changed_off);
+ }
+ }
+
+ @Override
public void onDataSaverChanged(boolean isDataSaving) {
refreshState(isDataSaving);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index da77dfd..73ce26f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -23,7 +23,9 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -389,6 +391,13 @@
return false;
}
+ Point realSize = new Point();
+ if (initialBounds == null) {
+ mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
+ .getRealSize(realSize);
+ initialBounds = new Rect(0, 0, realSize.x, realSize.y);
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index cd1a27f..473956f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -431,13 +431,6 @@
mIgnoreAltTabRelease = false;
mIterateTrigger.stopDozing();
-
- // Workaround for b/22542869, if the RecentsActivity is started again, but without going
- // through SystemUI, we need to reset the config launch flags to ensure that we do not
- // wait on the system to send a signal that was never queued.
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.reset();
}
@Override
@@ -453,6 +446,13 @@
mIsVisible = false;
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
+
+ // Workaround for b/22542869, if the RecentsActivity is started again, but without going
+ // through SystemUI, we need to reset the config launch flags to ensure that we do not
+ // wait on the system to send a signal that was never queued.
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ launchState.reset();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index dd825cb..a91bbd4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -105,6 +105,9 @@
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
if (screenPinningRequested) {
@@ -119,6 +122,9 @@
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
}
};
@@ -146,10 +152,6 @@
animStartedListener);
}
}
-
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
}
/**
@@ -278,7 +280,8 @@
} else {
layoutAlgorithm.getStackTransformScreenCoordinates(task, stackScroll, mTmpTransform,
null);
- specs.add(composeAnimationSpec(taskView, mTmpTransform, true /* addHeaderBitmap */));
+ specs.add(composeAnimationSpec(stackView, taskView, mTmpTransform,
+ true /* addHeaderBitmap */));
}
return specs;
}
@@ -299,7 +302,8 @@
} else {
layoutAlgorithm.getStackTransformScreenCoordinates(t, stackScroll,
mTmpTransform, null);
- specs.add(composeAnimationSpec(tv, mTmpTransform, true /* addHeaderBitmap */));
+ specs.add(composeAnimationSpec(stackView, tv, mTmpTransform,
+ true /* addHeaderBitmap */));
}
}
}
@@ -318,8 +322,8 @@
/**
* Composes a single animation spec for the given {@link TaskView}
*/
- private static AppTransitionAnimationSpec composeAnimationSpec(TaskView taskView,
- TaskViewTransform transform, boolean addHeaderBitmap) {
+ private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView,
+ TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
Bitmap b = null;
if (addHeaderBitmap) {
float scale = transform.scale;
@@ -341,6 +345,10 @@
Rect taskRect = new Rect();
transform.rect.round(taskRect);
+ if (stackView.getStack().getStackFrontMostTask(false /* includeFreeformTasks */) !=
+ taskView.getTask()) {
+ taskRect.bottom = 2 * Recents.getSystemServices().getDisplayRect().height();
+ }
return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index a5ed32a..6df5884 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -222,9 +222,11 @@
private Range mUnfocusedRange;
private Range mFocusedRange;
- // The initial offset from the top of the stack
+ // The initial offset from the top and bottom of the stack
@ViewDebug.ExportedProperty(category="recents")
private int mInitialTopPeekHeight;
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mInitialBottomPeekHeight;
// The offset from the top when scrolled to the top of the stack
@ViewDebug.ExportedProperty(category="recents")
@@ -322,6 +324,8 @@
res.getFloat(R.integer.recents_layout_unfocused_range_max));
mFocusState = getInitialFocusState();
mInitialTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_initial_top_peek_size);
+ mInitialBottomPeekHeight =
+ res.getDimensionPixelSize(R.dimen.recents_initial_bottom_peek_size);
mFocusedTopPeekHeight =
res.getDimensionPixelSize(R.dimen.recents_layout_focused_top_peek_size);
mFocusedBottomTaskPeekHeight =
@@ -508,10 +512,13 @@
float initialPeekOffsetNormX = mUnfocusedCurveInterpolator.getX(initialPeekOffsetPct);
float initialFocusedOffset = mStackRect.height() - mInitialTopPeekHeight -
(mHeaderBarHeight * 1f) + 1;
- float initialFocusedOffsetPct = (float) initialFocusedOffset / mStackRect.height();
+ float initialFocusedOffsetPct = initialFocusedOffset / mStackRect.height();
float initialFocusedNormX = mUnfocusedCurveInterpolator.getX(initialFocusedOffsetPct);
- int initialBottomOffset = mStackBottomOffset + mHeaderBarHeight;
- float initialBottomOffsetPct = (float) initialBottomOffset / mStackRect.height();
+ float initialBottomOffset = mStackBottomOffset +
+ (ssp.hasDockedTask()
+ ? mHeaderBarHeight
+ : mInitialBottomPeekHeight);
+ float initialBottomOffsetPct = initialBottomOffset / mStackRect.height();
float initialBottomNormX = mUnfocusedCurveInterpolator.getX(initialBottomOffsetPct);
/*
// If we want to offset the top card slightly
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 8f8683b..69dcabe 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -17,18 +17,31 @@
package com.android.systemui.shortcut;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
import android.os.RemoteException;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.IWindowManager;
import android.view.KeyEvent;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.DividerView;
+import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
import java.util.List;
import java.util.Set;
@@ -42,28 +55,70 @@
private static final String TAG = "ShortcutKeyDispatcher";
private ShortcutKeyServiceProxy mShortcutKeyServiceProxy = new ShortcutKeyServiceProxy(this);
- private IWindowManager windowManagerService = WindowManagerGlobal.getWindowManagerService();
+ private IWindowManager mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ private IActivityManager mActivityManager = ActivityManagerNative.getDefault();
protected final long META_MASK = ((long) KeyEvent.META_META_ON) << Integer.SIZE;
protected final long ALT_MASK = ((long) KeyEvent.META_ALT_ON) << Integer.SIZE;
protected final long CTRL_MASK = ((long) KeyEvent.META_CTRL_ON) << Integer.SIZE;
protected final long SHIFT_MASK = ((long) KeyEvent.META_SHIFT_ON) << Integer.SIZE;
+ protected final long SC_DOCK_LEFT = META_MASK | KeyEvent.KEYCODE_LEFT_BRACKET;
+ protected final long SC_DOCK_RIGHT = META_MASK | KeyEvent.KEYCODE_RIGHT_BRACKET;
+
/**
* Registers a shortcut key to window manager.
* @param shortcutCode packed representation of shortcut key code and meta information
*/
public void registerShortcutKey(long shortcutCode) {
try {
- windowManagerService.registerShortcutKey(shortcutCode, mShortcutKeyServiceProxy);
+ mWindowManagerService.registerShortcutKey(shortcutCode, mShortcutKeyServiceProxy);
} catch (RemoteException e) {
// Do nothing
}
}
@Override
- public void onShortcutKeyPressed(long shortcutCode) {}
+ public void onShortcutKeyPressed(long shortcutCode) {
+ int orientation = mContext.getResources().getConfiguration().orientation;
+ if ((shortcutCode == SC_DOCK_LEFT || shortcutCode == SC_DOCK_RIGHT)
+ && orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ handleDockKey(shortcutCode);
+ }
+ }
@Override
- public void start() {}
+ public void start() {
+ registerShortcutKey(SC_DOCK_LEFT);
+ registerShortcutKey(SC_DOCK_RIGHT);
+ }
+
+ private void handleDockKey(long shortcutCode) {
+ try {
+ int dockSide = mWindowManagerService.getDockedStackSide();
+ if (dockSide == WindowManager.DOCKED_INVALID) {
+ // If there is no window docked, we dock the top-most window.
+ Recents recents = getComponent(Recents.class);
+ int dockMode = (shortcutCode == SC_DOCK_LEFT)
+ ? ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ recents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, dockMode, null);
+ MetricsLogger.action(mContext, MetricsEvent.WINDOW_DOCK_SHORTCUTS);
+ } else {
+ // If there is already a docked window, we respond by resizing the docking pane.
+ DividerView dividerView = getComponent(Divider.class).getView();
+ DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
+ int dividerPosition = dividerView.getCurrentPosition();
+ DividerSnapAlgorithm.SnapTarget currentTarget =
+ snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
+ int increment = (shortcutCode == SC_DOCK_LEFT) ? -1 : 1;
+ DividerSnapAlgorithm.SnapTarget target = snapAlgorithm.cycleNonDismissTarget(
+ currentTarget, increment);
+ dividerView.startDragging(true /* animate */, false /* touching */);
+ dividerView.stopDragging(target.position, 0f, true /* avoidDismissStart */);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "handleDockKey() failed.");
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9f97c71..03cdcb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1172,13 +1172,8 @@
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- Point realSize = new Point();
- mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
- .getRealSize(realSize);
- Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
- ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
- initialBounds);
+ ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null);
} else {
EventBus.getDefault().send(new UndockingTaskEvent());
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index ea15b81..ff7ea27 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -68,6 +68,9 @@
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
+
+ private static final int CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS = 3000;
+
private int mSuspendPipResizingReason;
private static final float SCALE_FACTOR = 1.1f;
@@ -172,6 +175,12 @@
resizePinnedStack(mState);
}
};
+ private final Runnable mClosePipRunnable = new Runnable() {
+ @Override
+ public void run() {
+ closePip();
+ }
+ };
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -283,6 +292,7 @@
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onPipActivityClosed();
}
+ mHandler.removeCallbacks(mClosePipRunnable);
}
/**
@@ -545,6 +555,12 @@
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onMediaControllerChanged();
}
+ if (mPipMediaController == null) {
+ mHandler.postDelayed(mClosePipRunnable,
+ CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS);
+ } else {
+ mHandler.removeCallbacks(mClosePipRunnable);
+ }
}
}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index fb50df9..d36a1d7 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -1993,6 +1993,9 @@
// Settings -> Apps -> Gear -> Special access
SPECIAL_ACCESS = 351;
+ // Logs that the user docks window via shortcut key.
+ WINDOW_DOCK_SHORTCUTS = 352;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 4667172..c5a210c 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -150,6 +150,12 @@
int mNumTimeChanged;
/**
+ * The current set of user whitelisted apps for device idle mode, meaning these are allowed
+ * to freely schedule alarms.
+ */
+ int[] mDeviceIdleUserWhitelist = new int[0];
+
+ /**
* For each uid, this is the last time we dispatched an "allow while idle" alarm,
* used to determine the earliest we can dispatch the next such alarm.
*/
@@ -936,6 +942,7 @@
}
publishBinderService(Context.ALARM_SERVICE, mService);
+ publishLocalService(LocalService.class, new LocalService());
}
@Override
@@ -1251,14 +1258,6 @@
flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
}
- // If the caller is a core system component, and not calling to do work on behalf
- // of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. This means we
- // will allow these alarms to go off as normal even while idle, with no timing
- // restrictions.
- if (callingUid < Process.FIRST_APPLICATION_UID && workSource == null) {
- flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
- }
-
// If this is an exact time alarm, then it can't be batched with other alarms.
if (windowLength == AlarmManager.WINDOW_EXACT) {
flags |= AlarmManager.FLAG_STANDALONE;
@@ -1268,6 +1267,16 @@
// use it to wake early from idle if needed.
if (alarmClock != null) {
flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
+
+ // If the caller is a core system component or on the user's whitelist, and not calling
+ // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
+ // This means we will allow these alarms to go off as normal even while idle, with no
+ // timing restrictions.
+ } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
+ || Arrays.binarySearch(mDeviceIdleUserWhitelist,
+ UserHandle.getAppId(callingUid)) >= 0)) {
+ flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+ flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
}
setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
@@ -1344,6 +1353,12 @@
}
};
+ public final class LocalService {
+ public void setDeviceIdleUserWhitelist(int[] appids) {
+ setDeviceIdleUserWhitelistImpl(appids);
+ }
+ }
+
void dumpImpl(PrintWriter pw) {
synchronized (mLock) {
pw.println("Current Alarm Manager state:");
@@ -1386,6 +1401,7 @@
pw.print(" Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
pw.print(" Num time change events: "); pw.println(mNumTimeChanged);
+ pw.println(" mDeviceIdleUserWhitelist=" + Arrays.toString(mDeviceIdleUserWhitelist));
pw.println();
pw.println(" Next alarm clock information: ");
@@ -1678,6 +1694,12 @@
}
}
+ void setDeviceIdleUserWhitelistImpl(int[] appids) {
+ synchronized (mLock) {
+ mDeviceIdleUserWhitelist = appids;
+ }
+ }
+
AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
synchronized (mLock) {
return mNextAlarmClockForUser.get(userId);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 62fa7d5..7bf4b56 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -120,6 +120,7 @@
private AlarmManager mAlarmManager;
private IBatteryStats mBatteryStats;
private PowerManagerInternal mLocalPowerManager;
+ private AlarmManagerService.LocalService mLocalAlarmManager;
private INetworkPolicyManager mNetworkPolicyManager;
private DisplayManager mDisplayManager;
private SensorManager mSensorManager;
@@ -269,6 +270,17 @@
private int[] mPowerSaveWhitelistAllAppIdArray = new int[0];
/**
+ * App IDs that have been white-listed by the user to opt out of power save restrictions.
+ */
+ private final SparseBooleanArray mPowerSaveWhitelistUserAppIds = new SparseBooleanArray();
+
+ /**
+ * Current app IDs that are in the user power save white list. This array can
+ * be shared with others because it will not be modified once set.
+ */
+ private int[] mPowerSaveWhitelistUserAppIdArray = new int[0];
+
+ /**
* List of end times for UIDs that are temporarily marked as being allowed to access
* the network and acquire wakelocks. Times are in milliseconds.
*/
@@ -964,6 +976,10 @@
return getSystemPowerWhitelistInternal();
}
+ @Override public String[] getUserPowerWhitelist() {
+ return getUserPowerWhitelistInternal();
+ }
+
@Override public String[] getFullPowerWhitelistExceptIdle() {
return getFullPowerWhitelistExceptIdleInternal();
}
@@ -980,6 +996,10 @@
return getAppIdWhitelistInternal();
}
+ @Override public int[] getAppIdUserWhitelist() {
+ return getAppIdUserWhitelistInternal();
+ }
+
@Override public int[] getAppIdTempWhitelist() {
return getAppIdTempWhitelistInternal();
}
@@ -1161,6 +1181,7 @@
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mBatteryStats = BatteryStatsService.getService();
mLocalPowerManager = getLocalService(PowerManagerInternal.class);
+ mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mDisplayManager = (DisplayManager) getContext().getSystemService(
@@ -1227,6 +1248,7 @@
getContext().registerReceiver(mReceiver, filter);
mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
+ mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
mDisplayManager.registerDisplayListener(mDisplayListener, null);
updateDisplayLocked();
@@ -1291,6 +1313,17 @@
}
}
+ public String[] getUserPowerWhitelistInternal() {
+ synchronized (this) {
+ int size = mPowerSaveWhitelistUserApps.size();
+ String[] apps = new String[size];
+ for (int i = 0; i < mPowerSaveWhitelistUserApps.size(); i++) {
+ apps[i] = mPowerSaveWhitelistUserApps.keyAt(i);
+ }
+ return apps;
+ }
+ }
+
public String[] getFullPowerWhitelistExceptIdleInternal() {
synchronized (this) {
int size = mPowerSaveWhitelistAppsExceptIdle.size() + mPowerSaveWhitelistUserApps.size();
@@ -1351,6 +1384,12 @@
}
}
+ public int[] getAppIdUserWhitelistInternal() {
+ synchronized (this) {
+ return mPowerSaveWhitelistUserAppIdArray;
+ }
+ }
+
public int[] getAppIdTempWhitelistInternal() {
synchronized (this) {
return mTempWhitelistAppIdArray;
@@ -1993,11 +2032,15 @@
private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
ArrayMap<String, Integer> userApps, SparseBooleanArray outAppIds) {
outAppIds.clear();
- for (int i=0; i<systemApps.size(); i++) {
- outAppIds.put(systemApps.valueAt(i), true);
+ if (systemApps != null) {
+ for (int i = 0; i < systemApps.size(); i++) {
+ outAppIds.put(systemApps.valueAt(i), true);
+ }
}
- for (int i=0; i<userApps.size(); i++) {
- outAppIds.put(userApps.valueAt(i), true);
+ if (userApps != null) {
+ for (int i = 0; i < userApps.size(); i++) {
+ outAppIds.put(userApps.valueAt(i), true);
+ }
}
int size = outAppIds.size();
int[] appids = new int[size];
@@ -2012,6 +2055,8 @@
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds);
mPowerSaveWhitelistAllAppIdArray = buildAppIdArray(mPowerSaveWhitelistApps,
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);
+ mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null,
+ mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds);
if (mLocalPowerManager != null) {
if (DEBUG) {
Slog.d(TAG, "Setting wakelock whitelist to "
@@ -2019,6 +2064,13 @@
}
mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
}
+ if (mLocalAlarmManager != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Setting alarm whitelist to "
+ + Arrays.toString(mPowerSaveWhitelistUserAppIdArray));
+ }
+ mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
+ }
}
private void updateTempWhitelistAppIdsLocked() {
@@ -2536,6 +2588,15 @@
pw.println();
}
}
+ size = mPowerSaveWhitelistUserAppIds.size();
+ if (size > 0) {
+ pw.println(" Whitelist user app ids:");
+ for (int i = 0; i < size; i++) {
+ pw.print(" ");
+ pw.print(mPowerSaveWhitelistUserAppIds.keyAt(i));
+ pw.println();
+ }
+ }
size = mPowerSaveWhitelistAllAppIds.size();
if (size > 0) {
pw.println(" Whitelist all app ids:");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 28882de..71a1f97 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -32,6 +32,7 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFormatException;
+import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
@@ -39,6 +40,9 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.os.health.HealthStatsParceler;
+import android.os.health.HealthStatsWriter;
+import android.os.health.UidHealthStats;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.SignalStrength;
@@ -65,7 +69,9 @@
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* All information we are collecting about things that can happen that impact
@@ -1419,4 +1425,88 @@
}
}
}
+
+ /**
+ * Gets a snapshot of the system health for a particular uid.
+ */
+ @Override
+ public HealthStatsParceler takeUidSnapshot(int requestUid) {
+ if (requestUid != Binder.getCallingUid()) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ updateExternalStats("get-health-stats-for-uid",
+ BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ synchronized (mStats) {
+ return getHealthStatsForUidLocked(requestUid);
+ }
+ } catch (Exception ex) {
+ Slog.d(TAG, "Crashed while writing for takeUidSnapshot(" + requestUid + ")", ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Gets a snapshot of the system health for a number of uids.
+ */
+ @Override
+ public HealthStatsParceler[] takeUidSnapshots(int[] requestUids) {
+ if (!onlyCaller(requestUids)) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ }
+ long ident = Binder.clearCallingIdentity();
+ int i=-1;
+ try {
+ updateExternalStats("get-health-stats-for-uids",
+ BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ synchronized (mStats) {
+ final int N = requestUids.length;
+ final HealthStatsParceler[] results = new HealthStatsParceler[N];
+ for (i=0; i<N; i++) {
+ results[i] = getHealthStatsForUidLocked(requestUids[i]);
+ }
+ return results;
+ }
+ } catch (Exception ex) {
+ Slog.d(TAG, "Crashed while writing for takeUidSnapshots("
+ + Arrays.toString(requestUids) + ") i=" + i, ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Returns whether the Binder.getCallingUid is the only thing in requestUids.
+ */
+ private static boolean onlyCaller(int[] requestUids) {
+ final int caller = Binder.getCallingUid();
+ final int N = requestUids.length;
+ for (int i=0; i<N; i++) {
+ if (requestUids[i] != caller) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Gets a HealthStatsParceler for the given uid. You should probably call
+ * updateExternalStats first.
+ */
+ HealthStatsParceler getHealthStatsForUidLocked(int requestUid) {
+ final HealthStatsBatteryStatsWriter writer = new HealthStatsBatteryStatsWriter();
+ final HealthStatsWriter uidWriter = new HealthStatsWriter(UidHealthStats.CONSTANTS);
+ final BatteryStats.Uid uid = mStats.getUidStats().get(requestUid);
+ if (uid != null) {
+ writer.writeUid(uidWriter, mStats, uid);
+ }
+ return new HealthStatsParceler(uidWriter);
+ }
+
}
diff --git a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
new file mode 100644
index 0000000..39c6ce6
--- /dev/null
+++ b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2016 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.am;
+
+import android.os.BatteryStats;
+import static android.os.BatteryStats.STATS_SINCE_UNPLUGGED;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.health.HealthKeys;
+import android.os.health.HealthStatsParceler;
+import android.os.health.HealthStatsWriter;
+import android.os.health.PackageHealthStats;
+import android.os.health.ProcessHealthStats;
+import android.os.health.PidHealthStats;
+import android.os.health.ServiceHealthStats;
+import android.os.health.TimerStat;
+import android.os.health.UidHealthStats;
+import android.util.SparseArray;
+
+import java.util.Map;
+
+public class HealthStatsBatteryStatsWriter {
+
+ private final long mNowRealtime;
+ private final long mNowUptime;
+
+ public HealthStatsBatteryStatsWriter() {
+ mNowRealtime = SystemClock.elapsedRealtime();
+ mNowUptime = SystemClock.uptimeMillis();
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid into a HealthStatsWriter.
+ */
+ public void writeUid(HealthStatsWriter uidWriter, BatteryStats bs, BatteryStats.Uid uid) {
+ int N;
+ BatteryStats.Timer timer;
+ SparseArray<? extends BatteryStats.Uid.Sensor> sensors;
+ SparseArray<? extends BatteryStats.Uid.Pid> pids;
+ BatteryStats.ControllerActivityCounter controller;
+ long sum;
+
+ //
+ // It's a little odd for these first four to be here but it's not the end of the
+ // world. It would be easy enough to duplicate them somewhere else if this API
+ // grows.
+ //
+
+ // MEASUREMENT_REALTIME_BATTERY_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_REALTIME_BATTERY_MS,
+ bs.computeBatteryRealtime(mNowRealtime*1000, STATS_SINCE_UNPLUGGED)/1000);
+
+ // MEASUREMENT_UPTIME_BATTERY_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_UPTIME_BATTERY_MS,
+ bs.computeBatteryUptime(mNowUptime*1000, STATS_SINCE_UNPLUGGED)/1000);
+
+ // MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS,
+ bs.computeBatteryScreenOffRealtime(mNowRealtime*1000, STATS_SINCE_UNPLUGGED)/1000);
+
+ // MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS,
+ bs.computeBatteryScreenOffUptime(mNowUptime*1000, STATS_SINCE_UNPLUGGED)/1000);
+
+ //
+ // Now on to the real per-uid stats...
+ //
+
+ for (final Map.Entry<String,? extends BatteryStats.Uid.Wakelock> entry:
+ uid.getWakelockStats().entrySet()) {
+ final String key = entry.getKey();
+ final BatteryStats.Uid.Wakelock wakelock = entry.getValue();
+
+ // TIMERS_WAKELOCKS_FULL
+ timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_FULL);
+ addTimers(uidWriter, UidHealthStats.TIMERS_WAKELOCKS_FULL, key, timer);
+
+ // TIMERS_WAKELOCKS_PARTIAL
+ timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
+ addTimers(uidWriter, UidHealthStats.TIMERS_WAKELOCKS_PARTIAL, key, timer);
+
+ // TIMERS_WAKELOCKS_WINDOW
+ timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_WINDOW);
+ addTimers(uidWriter, UidHealthStats.TIMERS_WAKELOCKS_WINDOW, key, timer);
+
+ // TIMERS_WAKELOCKS_DRAW
+ timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_DRAW);
+ addTimers(uidWriter, UidHealthStats.TIMERS_WAKELOCKS_DRAW, key, timer);
+ }
+
+ // TIMERS_SYNCS
+ for (final Map.Entry<String,? extends BatteryStats.Timer> entry:
+ uid.getSyncStats().entrySet()) {
+ addTimers(uidWriter, UidHealthStats.TIMERS_SYNCS, entry.getKey(), entry.getValue());
+ }
+
+ // TIMERS_JOBS
+ for (final Map.Entry<String,? extends BatteryStats.Timer> entry:
+ uid.getJobStats().entrySet()) {
+ addTimers(uidWriter, UidHealthStats.TIMERS_JOBS, entry.getKey(), entry.getValue());
+ }
+
+ // TIMERS_SENSORS
+ sensors = uid.getSensorStats();
+ N = sensors.size();
+ for (int i=0; i<N; i++) {
+ int sensorId = sensors.keyAt(i);
+ // Battery Stats stores the GPS sensors with a bogus key in this API. Pull it out
+ // as a separate metric here so as to not expose that in the API.
+ if (sensorId == BatteryStats.Uid.Sensor.GPS) {
+ addTimer(uidWriter, UidHealthStats.TIMER_GPS_SENSOR, sensors.valueAt(i).getSensorTime());
+ } else {
+ addTimers(uidWriter, UidHealthStats.TIMERS_SENSORS, Integer.toString(sensorId),
+ sensors.valueAt(i).getSensorTime());
+ }
+ }
+
+ // STATS_PIDS
+ pids = uid.getPidStats();
+ N = sensors.size();
+ for (int i=0; i<N; i++) {
+ final HealthStatsWriter writer = new HealthStatsWriter(PidHealthStats.CONSTANTS);
+ writePid(writer, pids.valueAt(i));
+ uidWriter.addStats(UidHealthStats.STATS_PIDS, Integer.toString(pids.keyAt(i)), writer);
+ }
+
+ // STATS_PROCESSES
+ for (final Map.Entry<String,? extends BatteryStats.Uid.Proc> entry:
+ uid.getProcessStats().entrySet()) {
+ final HealthStatsWriter writer = new HealthStatsWriter(ProcessHealthStats.CONSTANTS);
+ writeProc(writer, entry.getValue());
+ uidWriter.addStats(UidHealthStats.STATS_PROCESSES, entry.getKey(), writer);
+ }
+
+ // STATS_PACKAGES
+ for (final Map.Entry<String,? extends BatteryStats.Uid.Pkg> entry:
+ uid.getPackageStats().entrySet()) {
+ final HealthStatsWriter writer = new HealthStatsWriter(PackageHealthStats.CONSTANTS);
+ writePkg(writer, entry.getValue());
+ uidWriter.addStats(UidHealthStats.STATS_PACKAGES, entry.getKey(), writer);
+ }
+
+ controller = uid.getWifiControllerActivity();
+ if (controller != null) {
+ // MEASUREMENT_WIFI_IDLE_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_IDLE_MS,
+ controller.getIdleTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_WIFI_RX_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_RX_MS,
+ controller.getRxTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_WIFI_TX_MS
+ sum = 0;
+ for (final BatteryStats.LongCounter counter: controller.getTxTimeCounters()) {
+ sum += counter.getCountLocked(STATS_SINCE_UNPLUGGED);
+ }
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_TX_MS, sum);
+ // MEASUREMENT_WIFI_POWER_MAMS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_POWER_MAMS,
+ controller.getPowerCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ }
+
+ controller = uid.getBluetoothControllerActivity();
+ if (controller != null) {
+ // MEASUREMENT_BLUETOOTH_IDLE_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_IDLE_MS,
+ controller.getIdleTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_BLUETOOTH_RX_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_RX_MS,
+ controller.getRxTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_BLUETOOTH_TX_MS
+ sum = 0;
+ for (final BatteryStats.LongCounter counter: controller.getTxTimeCounters()) {
+ sum += counter.getCountLocked(STATS_SINCE_UNPLUGGED);
+ }
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_TX_MS, sum);
+ // MEASUREMENT_BLUETOOTH_POWER_MAMS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_POWER_MAMS,
+ controller.getPowerCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ }
+
+ controller = uid.getModemControllerActivity();
+ if (controller != null) {
+ // MEASUREMENT_MOBILE_IDLE_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_IDLE_MS,
+ controller.getIdleTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_MOBILE_RX_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_RX_MS,
+ controller.getRxTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_MOBILE_TX_MS
+ sum = 0;
+ for (final BatteryStats.LongCounter counter: controller.getTxTimeCounters()) {
+ sum += counter.getCountLocked(STATS_SINCE_UNPLUGGED);
+ }
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_TX_MS, sum);
+ // MEASUREMENT_MOBILE_POWER_MAMS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_POWER_MAMS,
+ controller.getPowerCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ }
+
+ // MEASUREMENT_WIFI_RUNNING_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_RUNNING_MS,
+ uid.getWifiRunningTime(mNowRealtime, STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_FULL_LOCK_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_FULL_LOCK_MS,
+ uid.getFullWifiLockTime(mNowRealtime, STATS_SINCE_UNPLUGGED));
+
+ // TIMER_WIFI_SCAN
+ uidWriter.addTimer(UidHealthStats.TIMER_WIFI_SCAN,
+ uid.getWifiScanCount(STATS_SINCE_UNPLUGGED),
+ uid.getWifiScanTime(mNowRealtime, STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_MULTICAST_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_MULTICAST_MS,
+ uid.getWifiMulticastTime(mNowRealtime, STATS_SINCE_UNPLUGGED));
+
+ // TIMER_AUDIO
+ addTimer(uidWriter, UidHealthStats.TIMER_AUDIO, uid.getAudioTurnedOnTimer());
+
+ // TIMER_VIDEO
+ addTimer(uidWriter, UidHealthStats.TIMER_VIDEO, uid.getVideoTurnedOnTimer());
+
+ // TIMER_FLASHLIGHT
+ addTimer(uidWriter, UidHealthStats.TIMER_FLASHLIGHT, uid.getFlashlightTurnedOnTimer());
+
+ // TIMER_CAMERA
+ addTimer(uidWriter, UidHealthStats.TIMER_CAMERA, uid.getCameraTurnedOnTimer());
+
+ // TIMER_FOREGROUND_ACTIVITY
+ addTimer(uidWriter, UidHealthStats.TIMER_FOREGROUND_ACTIVITY, uid.getForegroundActivityTimer());
+
+ // TIMER_BLUETOOTH_SCAN
+ addTimer(uidWriter, UidHealthStats.TIMER_BLUETOOTH_SCAN, uid.getBluetoothScanTimer());
+
+ // TIMER_PROCESS_STATE_TOP_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_TOP_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_TOP));
+
+ // TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE));
+
+ // TIMER_PROCESS_STATE_TOP_SLEEPING_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_TOP_SLEEPING_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING));
+
+ // TIMER_PROCESS_STATE_FOREGROUND_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_FOREGROUND_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_FOREGROUND));
+
+ // TIMER_PROCESS_STATE_BACKGROUND_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_BACKGROUND_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_BACKGROUND));
+
+ // TIMER_PROCESS_STATE_CACHED_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_CACHED_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_CACHED));
+
+ // TIMER_VIBRATOR
+ addTimer(uidWriter, UidHealthStats.TIMER_VIBRATOR, uid.getVibratorOnTimer());
+
+ // MEASUREMENT_OTHER_USER_ACTIVITY_COUNT
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_OTHER_USER_ACTIVITY_COUNT,
+ uid.getUserActivityCount(PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT,
+ uid.getUserActivityCount(PowerManager.USER_ACTIVITY_EVENT_BUTTON,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT,
+ uid.getUserActivityCount(PowerManager.USER_ACTIVITY_EVENT_TOUCH,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_MOBILE_RX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_RX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_MOBILE_TX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_TX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_RX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_RX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_TX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_TX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BLUETOOTH_RX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_RX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BLUETOOTH_TX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_TX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_MOBILE_RX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_RX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_MOBILE_TX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_TX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_RX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_RX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_TX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_TX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BLUETOOTH_RX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_RX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_BT_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BLUETOOTH_TX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_TX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_BT_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // TIMER_MOBILE_RADIO_ACTIVE
+ uidWriter.addTimer(UidHealthStats.TIMER_MOBILE_RADIO_ACTIVE,
+ uid.getMobileRadioActiveCount(STATS_SINCE_UNPLUGGED),
+ uid.getMobileRadioActiveTime(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_USER_CPU_TIME_US
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_USER_CPU_TIME_US,
+ uid.getUserCpuTimeUs(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_SYSTEM_CPU_TIME_US
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_SYSTEM_CPU_TIME_US,
+ uid.getSystemCpuTimeUs(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_CPU_POWER_MAUS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_CPU_POWER_MAUS,
+ uid.getCpuPowerMaUs(STATS_SINCE_UNPLUGGED));
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid.Pid into a HealthStatsWriter.
+ */
+ public void writePid(HealthStatsWriter pidWriter, BatteryStats.Uid.Pid pid) {
+ if (pid == null) {
+ return;
+ }
+
+ // MEASUREMENT_WAKE_NESTING_COUNT
+ pidWriter.addMeasurement(PidHealthStats.MEASUREMENT_WAKE_NESTING_COUNT, pid.mWakeNesting);
+
+ // MEASUREMENT_WAKE_SUM_MS
+ pidWriter.addMeasurement(PidHealthStats.MEASUREMENT_WAKE_SUM_MS, pid.mWakeSumMs);
+
+ // MEASUREMENT_WAKE_START_MS
+ pidWriter.addMeasurement(PidHealthStats.MEASUREMENT_WAKE_SUM_MS, pid.mWakeStartMs);
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid.Proc into a HealthStatsWriter.
+ */
+ public void writeProc(HealthStatsWriter procWriter, BatteryStats.Uid.Proc proc) {
+ // MEASUREMENT_USER_TIME_MS
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_USER_TIME_MS,
+ proc.getUserTime(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_SYSTEM_TIME_MS
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_SYSTEM_TIME_MS,
+ proc.getSystemTime(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_STARTS_COUNT
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_STARTS_COUNT,
+ proc.getStarts(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_CRASHES_COUNT
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_CRASHES_COUNT,
+ proc.getNumCrashes(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_ANR_COUNT
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_ANR_COUNT,
+ proc.getNumAnrs(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_FOREGROUND_MS
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_FOREGROUND_MS,
+ proc.getForegroundTime(STATS_SINCE_UNPLUGGED));
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid.Pkg into a HealthStatsWriter.
+ */
+ public void writePkg(HealthStatsWriter pkgWriter, BatteryStats.Uid.Pkg pkg) {
+ // STATS_SERVICES
+ for (final Map.Entry<String,? extends BatteryStats.Uid.Pkg.Serv> entry:
+ pkg.getServiceStats().entrySet()) {
+ final HealthStatsWriter writer = new HealthStatsWriter(ServiceHealthStats.CONSTANTS);
+ writeServ(writer, entry.getValue());
+ pkgWriter.addStats(PackageHealthStats.STATS_SERVICES, entry.getKey(), writer);
+ }
+
+ // MEASUREMENTS_WAKEUP_ALARMS_COUNT
+ for (final Map.Entry<String,? extends BatteryStats.Counter> entry:
+ pkg.getWakeupAlarmStats().entrySet()) {
+ final BatteryStats.Counter counter = entry.getValue();
+ if (counter != null) {
+ pkgWriter.addMeasurements(PackageHealthStats.MEASUREMENTS_WAKEUP_ALARMS_COUNT,
+ entry.getKey(), counter.getCountLocked(STATS_SINCE_UNPLUGGED));
+ }
+ }
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid.Pkg.Serv into a HealthStatsWriter.
+ */
+ public void writeServ(HealthStatsWriter servWriter, BatteryStats.Uid.Pkg.Serv serv) {
+ // MEASUREMENT_START_SERVICE_COUNT
+ servWriter.addMeasurement(ServiceHealthStats.MEASUREMENT_START_SERVICE_COUNT,
+ serv.getStarts(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_LAUNCH_COUNT
+ servWriter.addMeasurement(ServiceHealthStats.MEASUREMENT_LAUNCH_COUNT,
+ serv.getLaunches(STATS_SINCE_UNPLUGGED));
+ }
+
+ /**
+ * Adds a BatteryStats.Timer into a HealthStatsWriter. Safe to pass a null timer.
+ */
+ private void addTimer(HealthStatsWriter writer, int key, BatteryStats.Timer timer) {
+ if (timer != null) {
+ writer.addTimer(key, timer.getCountLocked(STATS_SINCE_UNPLUGGED),
+ timer.getTotalTimeLocked(mNowRealtime, STATS_SINCE_UNPLUGGED));
+ }
+ }
+
+ /**
+ * Adds a named BatteryStats.Timer into a HealthStatsWriter. Safe to pass a null timer.
+ */
+ private void addTimers(HealthStatsWriter writer, int key, String name,
+ BatteryStats.Timer timer) {
+ if (timer != null) {
+ writer.addTimers(key, name, new TimerStat(timer.getCountLocked(STATS_SINCE_UNPLUGGED),
+ timer.getTotalTimeLocked(mNowRealtime, STATS_SINCE_UNPLUGGED)));
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 95a9875..e1d208e 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -192,6 +192,13 @@
*/
private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds
+ /**
+ * Generate job ids in the range [MIN_SYNC_JOB_ID, MAX_SYNC_JOB_ID) to avoid conflicts with
+ * other jobs scheduled by the system process.
+ */
+ private static final int MIN_SYNC_JOB_ID = 100000;
+ private static final int MAX_SYNC_JOB_ID = 110000;
+
private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
@@ -249,7 +256,7 @@
synchronized (mScheduledSyncs) {
int newJobId;
do {
- newJobId = mRand.nextInt(Integer.MAX_VALUE);
+ newJobId = MIN_SYNC_JOB_ID + mRand.nextInt(MAX_SYNC_JOB_ID - MIN_SYNC_JOB_ID);
} while (isJobIdInUseLockedH(newJobId));
return newJobId;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 02c786e..516602e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1444,16 +1444,7 @@
*/
@Override
public void unregisterListener(INotificationListener token, int userid) {
- final long identity = Binder.clearCallingIdentity();
- try {
- if(mRankerServices.checkServiceTokenLocked(token) != null) {
- mRankerServices.unregisterService(token, userid);
- } else {
- mListeners.unregisterService(token, userid);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ mListeners.unregisterService(token, userid);
}
/**
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 8982632..9d8def6 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -56,6 +56,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
@@ -92,8 +93,6 @@
/**
* TODO:
*
- * - Implement launchShortcut
- *
* - Detect when already registered instances are passed to APIs again, which might break
* internal bitmap handling.
*
@@ -103,8 +102,6 @@
*
* - Pinned per each launcher package (multiple launchers)
*
- * - Load config from settings
- *
* - Make save async (should we?)
*
* - Scan and remove orphan bitmaps (just in case).
@@ -117,11 +114,26 @@
private static final boolean DEBUG = true; // STOPSHIP if true
private static final boolean DEBUG_LOAD = true; // STOPSHIP if true
- private static final int DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
- private static final int DEFAULT_MAX_DAILY_UPDATES = 10;
- private static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
- private static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
- private static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
+ @VisibleForTesting
+ static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_DAILY_UPDATES = 10;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
+
+ @VisibleForTesting
+ static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
+
+ @VisibleForTesting
+ static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
@@ -158,6 +170,44 @@
private static final String ATTR_ICON_RES = "icon-res";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
+ @VisibleForTesting
+ interface ConfigConstants {
+ /**
+ * Key name for the throttling reset interval, in seconds. (long)
+ */
+ String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
+
+ /**
+ * Key name for the max number of modifying API calls per app for every interval. (int)
+ */
+ String KEY_MAX_DAILY_UPDATES = "max_daily_updates";
+
+ /**
+ * Key name for the max icon dimensions in DP, for non-low-memory devices.
+ */
+ String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
+
+ /**
+ * Key name for the max icon dimensions in DP, for low-memory devices.
+ */
+ String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
+
+ /**
+ * Key name for the max dynamic shortcuts per app. (int)
+ */
+ String KEY_MAX_SHORTCUTS = "max_shortcuts";
+
+ /**
+ * Key name for icom compression quality, 0-100.
+ */
+ String KEY_ICON_QUALITY = "icon_quality";
+
+ /**
+ * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
+ */
+ String KEY_ICON_FORMAT = "icon_format";
+ }
+
private final Context mContext;
private final Object mLock = new Object();
@@ -416,9 +466,8 @@
*/
private int mMaxIconDimension;
- private CompressFormat mIconPersistFormat = CompressFormat.PNG;
-
- private int mIconPersistQuality = 100;
+ private CompressFormat mIconPersistFormat;
+ private int mIconPersistQuality;
public ShortcutService(Context context) {
mContext = Preconditions.checkNotNull(context);
@@ -498,23 +547,76 @@
*/
private void initialize() {
synchronized (mLock) {
- injectLoadConfigurationLocked();
+ loadConfigurationLocked();
loadBaseStateLocked();
}
}
- // Test overrides it to inject different values.
- @VisibleForTesting
- void injectLoadConfigurationLocked() {
- mResetInterval = DEFAULT_RESET_INTERVAL_SEC * 1000L;
- mMaxDailyUpdates = DEFAULT_MAX_DAILY_UPDATES;
- mMaxDynamicShortcuts = DEFAULT_MAX_SHORTCUTS_PER_APP;
+ /**
+ * Load the configuration from Settings.
+ */
+ private void loadConfigurationLocked() {
+ updateConfigurationLocked(injectShortcutManagerConstants());
+ }
- final int iconDimensionDp = (injectIsLowRamDevice()
- ? DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP : DEFAULT_MAX_ICON_DIMENSION_DP);
- mMaxIconDimension =
- (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, iconDimensionDp,
- mContext.getResources().getDisplayMetrics());
+ /**
+ * Load the configuration from Settings.
+ */
+ @VisibleForTesting
+ boolean updateConfigurationLocked(String config) {
+ boolean result = true;
+
+ final KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(config);
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on
+ // with defaults.
+ Slog.e(TAG, "Bad shortcut manager settings", e);
+ result = false;
+ }
+
+ mResetInterval = parser.getLong(
+ ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
+ * 1000L;
+
+ mMaxDailyUpdates = (int) parser.getLong(
+ ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES);
+
+ mMaxDynamicShortcuts = (int) parser.getLong(
+ ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP);
+
+ final int iconDimensionDp = injectIsLowRamDevice()
+ ? (int) parser.getLong(
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM,
+ DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP)
+ : (int) parser.getLong(
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP,
+ DEFAULT_MAX_ICON_DIMENSION_DP);
+
+ mMaxIconDimension = injectDipToPixel(iconDimensionDp);
+
+ mIconPersistFormat = CompressFormat.valueOf(
+ parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
+
+ mIconPersistQuality = (int) parser.getLong(
+ ConfigConstants.KEY_ICON_QUALITY,
+ DEFAULT_ICON_PERSIST_QUALITY);
+
+ return result;
+ }
+
+ @VisibleForTesting
+ String injectShortcutManagerConstants() {
+ return android.provider.Settings.Global.getString(
+ mContext.getContentResolver(),
+ android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
+ }
+
+ @VisibleForTesting
+ int injectDipToPixel(int dip) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
+ mContext.getResources().getDisplayMetrics());
}
// === Persisting ===
@@ -1829,14 +1931,27 @@
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
- switch(cmd) {
+ int ret = 1;
+ switch (cmd) {
case "reset-package-throttling":
- return handleResetPackageThrottling();
+ ret = handleResetPackageThrottling();
+ break;
case "reset-throttling":
- return handleResetThrottling();
+ ret = handleResetThrottling();
+ break;
+ case "override-config":
+ ret = handleOverrideConfig();
+ break;
+ case "reset-config":
+ ret = handleResetConfig();
+ break;
default:
return handleDefaultCommands(cmd);
}
+ if (ret == 0) {
+ pw.println("Success");
+ }
+ return ret;
}
@Override
@@ -1850,6 +1965,12 @@
pw.println("cmd shortcut reset-throttling");
pw.println(" Reset throttling for all packages and users");
pw.println();
+ pw.println("cmd shortcut override-config CONFIG");
+ pw.println(" Override the configuration for testing (will last until reboot)");
+ pw.println();
+ pw.println("cmd shortcut reset-config");
+ pw.println(" Reset the configuration set with \"update-config\"");
+ pw.println();
}
private int handleResetThrottling() {
@@ -1881,6 +2002,26 @@
return 0;
}
+
+ private int handleOverrideConfig() {
+ final PrintWriter pw = getOutPrintWriter();
+ final String config = getNextArgRequired();
+
+ synchronized (mLock) {
+ if (!updateConfigurationLocked(config)) {
+ pw.println("override-config failed. See logcat for details.");
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ private int handleResetConfig() {
+ synchronized (mLock) {
+ loadConfigurationLocked();
+ }
+ return 0;
+ }
}
// === Unit test support ===
@@ -1903,6 +2044,7 @@
return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
}
+ @VisibleForTesting
boolean injectIsLowRamDevice() {
return ActivityManager.isLowRamDeviceStatic();
}
@@ -1917,22 +2059,32 @@
}
@VisibleForTesting
- void setMaxDynamicShortcutsForTest(int max) {
- mMaxDynamicShortcuts = max;
+ int getMaxDynamicShortcutsForTest() {
+ return mMaxDynamicShortcuts;
}
@VisibleForTesting
- void setMaxDailyUpdatesForTest(int max) {
- mMaxDailyUpdates = max;
+ int getMaxDailyUpdatesForTest() {
+ return mMaxDailyUpdates;
}
@VisibleForTesting
- void setMaxIconDimensionForTest(int dimension) {
- mMaxIconDimension = dimension;
+ long getResetIntervalForTest() {
+ return mResetInterval;
}
@VisibleForTesting
- public void setResetIntervalForTest(long interval) {
- mResetInterval = interval;
+ int getMaxIconDimensionForTest() {
+ return mMaxIconDimension;
+ }
+
+ @VisibleForTesting
+ CompressFormat getIconPersistFormatForTest() {
+ return mIconPersistFormat;
+ }
+
+ @VisibleForTesting
+ int getIconPersistQualityForTest() {
+ return mIconPersistQuality;
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5490260..8206bda 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -50,6 +50,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.SELinux;
import android.os.ServiceManager;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -758,23 +759,28 @@
}
@Override
- public ParcelFileDescriptor getUserIcon(int userId) {
+ public ParcelFileDescriptor getUserIcon(int targetUserId) {
String iconPath;
synchronized (mPackagesLock) {
- UserInfo info = getUserInfoNoChecks(userId);
- if (info == null || info.partial) {
- Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
+ UserInfo targetUserInfo = getUserInfoNoChecks(targetUserId);
+ if (targetUserInfo == null || targetUserInfo.partial) {
+ Slog.w(LOG_TAG, "getUserIcon: unknown user #" + targetUserId);
return null;
}
- int callingGroupId = getUserInfoNoChecks(UserHandle.getCallingUserId()).profileGroupId;
- if (callingGroupId == UserInfo.NO_PROFILE_GROUP_ID
- || callingGroupId != info.profileGroupId) {
+
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int callingGroupId = getUserInfoNoChecks(callingUserId).profileGroupId;
+ final int targetGroupId = targetUserInfo.profileGroupId;
+ final boolean sameGroup = (callingGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && callingGroupId == targetGroupId);
+ if ((callingUserId != targetUserId) && !sameGroup) {
checkManageUsersPermission("get the icon of a user who is not related");
}
- if (info.iconPath == null) {
+
+ if (targetUserInfo.iconPath == null) {
return null;
}
- iconPath = info.iconPath;
+ iconPath = targetUserInfo.iconPath;
}
try {
@@ -1229,7 +1235,7 @@
}
FileOutputStream os;
if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(tmp))
- && tmp.renameTo(file)) {
+ && tmp.renameTo(file) && SELinux.restorecon(file)) {
info.iconPath = file.getAbsolutePath();
}
try {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9a75dc9..d218afc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1642,7 +1642,7 @@
}
if (!TextUtils.isEmpty(mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT))) {
- Slog.wtf(LOG_TAG, "Trying to set ro.device_owner, but it has already been set?");
+ Slog.w(LOG_TAG, "Trying to set ro.device_owner, but it has already been set?");
} else {
if (mOwners.hasDeviceOwner()) {
mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "true");
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 21daa1b..036cc66 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -27,6 +27,7 @@
import android.content.pm.ShortcutServiceInternal;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -42,6 +43,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.ShortcutService.ConfigConstants;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
import libcore.io.IoUtils;
@@ -114,11 +116,20 @@
}
@Override
- void injectLoadConfigurationLocked() {
- setResetIntervalForTest(INTERVAL);
- setMaxDynamicShortcutsForTest(MAX_SHORTCUTS);
- setMaxDailyUpdatesForTest(MAX_DAILY_UPDATES);
- setMaxIconDimensionForTest(MAX_ICON_DIMENSION);
+ String injectShortcutManagerConstants() {
+ return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=" + MAX_SHORTCUTS + ","
+ + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=" + MAX_DAILY_UPDATES + ","
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=" + MAX_ICON_DIMENSION + ","
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "="
+ + MAX_ICON_DIMENSION_LOWRAM + ","
+ + ConfigConstants.KEY_ICON_FORMAT + "=PNG,"
+ + ConfigConstants.KEY_ICON_QUALITY + "=100";
+ }
+
+ @Override
+ int injectDipToPixel(int dip) {
+ return dip;
}
@Override
@@ -151,6 +162,11 @@
void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
// Can't check
}
+
+ @Override
+ boolean injectIsLowRamDevice() {
+ return mInjectdIsLowRamDevice;
+ }
}
/** ShortcutManager with injection override methods. */
@@ -186,6 +202,8 @@
private long mInjectedCurrentTimeLillis;
+ private boolean mInjectdIsLowRamDevice;
+
private int mInjectedCallingUid;
private String mInjectedClientPackage;
@@ -216,6 +234,8 @@
private static final int MAX_ICON_DIMENSION = 128;
+ private static final int MAX_ICON_DIMENSION_LOWRAM = 32;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -676,6 +696,44 @@
// TODO Add various broken cases.
}
+ public void testLoadConfig() {
+ mService.updateConfigurationLocked(
+ ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
+ + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=5,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ + ConfigConstants.KEY_ICON_FORMAT + "=WEBP,"
+ + ConfigConstants.KEY_ICON_QUALITY + "=75");
+ assertEquals(123000, mService.getResetIntervalForTest());
+ assertEquals(4, mService.getMaxDynamicShortcutsForTest());
+ assertEquals(5, mService.getMaxDailyUpdatesForTest());
+ assertEquals(100, mService.getMaxIconDimensionForTest());
+ assertEquals(CompressFormat.WEBP, mService.getIconPersistFormatForTest());
+ assertEquals(75, mService.getIconPersistQualityForTest());
+
+ mInjectdIsLowRamDevice = true;
+ mService.updateConfigurationLocked(
+ ConfigConstants.KEY_MAX_ICON_DIMENSION_DP + "=100,"
+ + ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM + "=50,"
+ + ConfigConstants.KEY_ICON_FORMAT + "=JPEG");
+ assertEquals(ShortcutService.DEFAULT_RESET_INTERVAL_SEC * 1000,
+ mService.getResetIntervalForTest());
+
+ assertEquals(ShortcutService.DEFAULT_MAX_SHORTCUTS_PER_APP,
+ mService.getMaxDynamicShortcutsForTest());
+
+ assertEquals(ShortcutService.DEFAULT_MAX_DAILY_UPDATES,
+ mService.getMaxDailyUpdatesForTest());
+
+ assertEquals(50, mService.getMaxIconDimensionForTest());
+
+ assertEquals(CompressFormat.JPEG, mService.getIconPersistFormatForTest());
+
+ assertEquals(ShortcutService.DEFAULT_ICON_PERSIST_QUALITY,
+ mService.getIconPersistQualityForTest());
+ }
+
// === Test for app side APIs ===
/** Test for {@link android.content.pm.ShortcutManager#getMaxDynamicShortcutCount()} */
diff --git a/services/usb/java/com/android/server/usb/MtpNotificationManager.java b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
index 17039bb..101e200 100644
--- a/services/usb/java/com/android/server/usb/MtpNotificationManager.java
+++ b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
@@ -101,8 +102,8 @@
TAG, device.getDeviceId(), notification);
}
- void hideNotification(UsbDevice device) {
- mContext.getSystemService(NotificationManager.class).cancel(TAG, device.getDeviceId());
+ void hideNotification(int deviceId) {
+ mContext.getSystemService(NotificationManager.class).cancel(TAG, deviceId);
}
private class Receiver extends BroadcastReceiver {
@@ -121,7 +122,13 @@
}
}
- static boolean isMtpDevice(UsbDevice device) {
+ static boolean shouldShowNotification(PackageManager packageManager, UsbDevice device) {
+ // We don't show MTP notification for devices that has FEATURE_AUTOMOTIVE.
+ return !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) &&
+ isMtpDevice(device);
+ }
+
+ private static boolean isMtpDevice(UsbDevice device) {
for (int i = 0; i < device.getInterfaceCount(); i++) {
final UsbInterface usbInterface = device.getInterface(i);
if ((usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE &&
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index c4d7336..de9ede3 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -738,7 +738,7 @@
// Send broadcast to running activity with registered intent
mUserContext.sendBroadcast(intent);
- if (MtpNotificationManager.isMtpDevice(device)) {
+ if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) {
// Show notification if the device is MTP storage.
mMtpNotificationManager.showNotification(device);
} else {
@@ -769,9 +769,7 @@
if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- if (MtpNotificationManager.isMtpDevice(device)) {
- mMtpNotificationManager.hideNotification(device);
- }
+ mMtpNotificationManager.hideNotification(device.getDeviceId());
}
public void accessoryAttached(UsbAccessory accessory) {
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 57a7692..d311b3d 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -90,6 +90,7 @@
proto/TableProtoSerializer_test.cpp \
split/TableSplitter_test.cpp \
util/BigBuffer_test.cpp \
+ util/Files_test.cpp \
util/Maybe_test.cpp \
util/StringPiece_test.cpp \
util/Util_test.cpp \
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 4d1db5b..03ca42b 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -77,7 +77,7 @@
ResourceType type;
std::u16string entry;
- ResourceName() = default;
+ ResourceName() : type(ResourceType::kRaw) {}
ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e);
bool isValid() const;
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 5003d96..418a802 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -729,12 +729,18 @@
std::string outPath = mOptions.generateJavaClassPath.value();
file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
- file::mkdirs(outPath);
+ if (!file::mkdirs(outPath)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to create directory '" << outPath << "'");
+ return false;
+ }
+
file::appendPath(&outPath, "R.java");
std::ofstream fout(outPath, std::ofstream::binary);
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
}
@@ -743,6 +749,11 @@
mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
return false;
}
+
+ if (!fout) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
+ }
return true;
}
@@ -754,12 +765,18 @@
std::string outPath = mOptions.generateJavaClassPath.value();
file::appendPath(&outPath,
file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
- file::mkdirs(outPath);
+ if (!file::mkdirs(outPath)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to create directory '" << outPath << "'");
+ return false;
+ }
+
file::appendPath(&outPath, "Manifest.java");
std::ofstream fout(outPath, std::ofstream::binary);
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
}
@@ -770,7 +787,8 @@
}
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
}
return true;
@@ -781,15 +799,18 @@
return true;
}
- std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
+ const std::string& outPath = mOptions.generateProguardRulesPath.value();
+ std::ofstream fout(outPath, std::ofstream::binary);
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to open '" << outPath << "': " << strerror(errno));
return false;
}
proguard::writeKeepSet(&fout, keepSet);
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
}
return true;
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 04e8199..6428e98 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -96,7 +96,7 @@
const char* start = path.begin();
const char* end = path.end();
for (const char* current = start; current != end; ++current) {
- if (*current == sDirSep) {
+ if (*current == sDirSep && current != start) {
StringPiece parentPath(start, current - start);
int result = mkdirImpl(parentPath);
if (result < 0 && errno != EEXIST) {
@@ -139,6 +139,20 @@
return {};
}
+void appendPath(std::string* base, StringPiece part) {
+ assert(base);
+ const bool baseHasTrailingSep = (!base->empty() && *(base->end() - 1) == sDirSep);
+ const bool partHasLeadingSep = (!part.empty() && *(part.begin()) == sDirSep);
+ if (baseHasTrailingSep && partHasLeadingSep) {
+ // Remove the part's leading sep
+ part = part.substr(1, part.size() - 1);
+ } else if (!baseHasTrailingSep && !partHasLeadingSep) {
+ // None of the pieces has a separator.
+ *base += sDirSep;
+ }
+ base->append(part.data(), part.size());
+}
+
std::string packageToPath(const StringPiece& package) {
std::string outPath;
for (StringPiece part : util::tokenize<char>(package, '.')) {
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index c58ba5d..c2e6115 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -61,14 +61,7 @@
/*
* Appends a path to `base`, separated by the directory separator.
*/
-void appendPath(std::string* base, const StringPiece& part);
-
-/*
- * Appends a series of paths to `base`, separated by the
- * system directory separator.
- */
-template <typename... Ts >
-void appendPath(std::string* base, const StringPiece& part, const Ts&... parts);
+void appendPath(std::string* base, StringPiece part);
/*
* Makes all the directories in `path`. The last element in the path
@@ -139,20 +132,6 @@
std::vector<std::string> mPatternTokens;
};
-inline void appendPath(std::string* base, const StringPiece& part) {
- assert(base);
- *base += sDirSep;
- base->append(part.data(), part.size());
-}
-
-template <typename... Ts >
-void appendPath(std::string* base, const StringPiece& part, const Ts&... parts) {
- assert(base);
- *base += sDirSep;
- base->append(part.data(), part.size());
- appendPath(base, parts...);
-}
-
} // namespace file
} // namespace aapt
diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
new file mode 100644
index 0000000..efb0459
--- /dev/null
+++ b/tools/aapt2/util/Files_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "test/Test.h"
+#include "util/Files.h"
+
+#include <sstream>
+
+namespace aapt {
+namespace file {
+
+class FilesTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ std::stringstream builder;
+ builder << "hello" << sDirSep << "there";
+ mExpectedPath = builder.str();
+ }
+
+protected:
+ std::string mExpectedPath;
+};
+
+TEST_F(FilesTest, appendPath) {
+ std::string base = "hello";
+ appendPath(&base, "there");
+ EXPECT_EQ(mExpectedPath, base);
+}
+
+TEST_F(FilesTest, appendPathWithLeadingOrTrailingSeparators) {
+ std::string base = "hello/";
+ appendPath(&base, "there");
+ EXPECT_EQ(mExpectedPath, base);
+
+ base = "hello";
+ appendPath(&base, "/there");
+ EXPECT_EQ(mExpectedPath, base);
+
+ base = "hello/";
+ appendPath(&base, "/there");
+ EXPECT_EQ(mExpectedPath, base);
+}
+
+} // namespace files
+} // namespace aapt