diff options
165 files changed, 5913 insertions, 2895 deletions
@@ -344,6 +344,7 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/ISms.aidl \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ telephony/java/com/android/internal/telephony/ISub.aidl \ + telephony/java/com/android/internal/telephony/IMms.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ @@ -361,7 +362,7 @@ LOCAL_INTERMEDIATE_SOURCES := \ $(framework_res_source_path)/com/android/internal/R.java LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt core core-junit ext okhttp +LOCAL_JAVA_LIBRARIES := core-libart conscrypt okhttp core-junit bouncycastle ext LOCAL_MODULE := framework-base @@ -589,9 +590,9 @@ framework_docs_LOCAL_INTERMEDIATE_SOURCES := \ $(framework_res_source_path)/com/android/internal/R.java framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \ - bouncycastle \ + core-libart \ conscrypt \ - core \ + bouncycastle \ okhttp \ ext \ framework \ @@ -633,7 +634,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -since $(SRC_API_DIR)/17.txt 17 \ -since $(SRC_API_DIR)/18.txt 18 \ -since $(SRC_API_DIR)/19.txt 19 \ - -werror -hide 113 \ + -werror -hide 111 -hide 113 \ -overview $(LOCAL_PATH)/core/java/overview.html framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \ @@ -902,7 +903,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(ext_src_files) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core +LOCAL_JAVA_LIBRARIES := core-libart LOCAL_JAVA_RESOURCE_DIRS := $(ext_res_dirs) LOCAL_MODULE_TAGS := optional LOCAL_MODULE := ext diff --git a/api/current.txt b/api/current.txt index 2912882..65589d4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -580,6 +580,7 @@ package android { field public static final int fromXScale = 16843202; // 0x10101c2 field public static final int fromYDelta = 16843208; // 0x10101c8 field public static final int fromYScale = 16843204; // 0x10101c4 + field public static final int fullBackupOnly = 16843893; // 0x1010475 field public static final int fullBright = 16842954; // 0x10100ca field public static final int fullDark = 16842950; // 0x10100c6 field public static final int functionalTest = 16842787; // 0x1010023 @@ -5194,6 +5195,7 @@ package android.app.admin { method public java.lang.CharSequence onDisableRequested(android.content.Context, android.content.Intent); method public void onDisabled(android.content.Context, android.content.Intent); method public void onEnabled(android.content.Context, android.content.Intent); + method public void onLockTaskModeChanged(android.content.Context, android.content.Intent, boolean, java.lang.String); method public void onPasswordChanged(android.content.Context, android.content.Intent); method public void onPasswordExpiring(android.content.Context, android.content.Intent); method public void onPasswordFailed(android.content.Context, android.content.Intent); @@ -5203,6 +5205,7 @@ package android.app.admin { field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED"; + field public static final java.lang.String ACTION_LOCK_TASK_CHANGED = "android.app.action.ACTION_LOCK_TASK_CHANGED"; field public static final java.lang.String ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED"; field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING"; field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED"; @@ -5210,6 +5213,8 @@ package android.app.admin { field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.ACTION_PROFILE_PROVISIONING_COMPLETE"; field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin"; field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING"; + field public static final java.lang.String EXTRA_LOCK_TASK_ENTERING = "android.app.extra.LOCK_TASK_ENTERING"; + field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; } public class DevicePolicyManager { @@ -5217,7 +5222,7 @@ package android.app.admin { method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName); method public void addUserRestriction(android.content.ComponentName, java.lang.String); method public void clearCrossProfileIntentFilters(android.content.ComponentName); - method public void clearDeviceOwnerApp(); + method public void clearDeviceOwnerApp(java.lang.String); method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String); method public void clearUserRestriction(android.content.ComponentName, java.lang.String); method public android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle); @@ -5244,7 +5249,10 @@ package android.app.admin { method public int getPasswordQuality(android.content.ComponentName); method public boolean getStorageEncryption(android.content.ComponentName); method public int getStorageEncryptionStatus(); + method public boolean hasAnyCaCertsInstalled(); + method public boolean hasCaCertInstalled(byte[]); method public boolean hasGrantedPolicy(android.content.ComponentName, int); + method public boolean installCaCert(android.content.ComponentName, byte[]); method public boolean isActivePasswordSufficient(); method public boolean isAdminActive(android.content.ComponentName); method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String); @@ -5282,6 +5290,7 @@ package android.app.admin { method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName); method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public int setStorageEncryption(android.content.ComponentName, boolean); + method public void uninstallCaCert(android.content.ComponentName, byte[]); method public void wipeData(int); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE"; @@ -8128,6 +8137,7 @@ package android.content.pm { field public static final int FLAG_DEBUGGABLE = 2; // 0x2 field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000 field public static final int FLAG_FACTORY_TEST = 16; // 0x10 + field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000 field public static final int FLAG_HAS_CODE = 4; // 0x4 field public static final int FLAG_INSTALLED = 8388608; // 0x800000 field public static final int FLAG_IS_DATA_ONLY = 16777216; // 0x1000000 @@ -13729,7 +13739,9 @@ package android.media { field public static final int CHANNEL_OUT_QUAD = 204; // 0xcc field public static final int CHANNEL_OUT_STEREO = 12; // 0xc field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c + field public static final int ENCODING_AC3 = 5; // 0x5 field public static final int ENCODING_DEFAULT = 1; // 0x1 + field public static final int ENCODING_E_AC3 = 6; // 0x6 field public static final int ENCODING_INVALID = 0; // 0x0 field public static final int ENCODING_PCM_16BIT = 2; // 0x2 field public static final int ENCODING_PCM_8BIT = 3; // 0x3 @@ -13803,6 +13815,7 @@ package android.media { field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0 field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1 field public static final int ERROR = -1; // 0xffffffff + field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE"; field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"; field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE"; @@ -15847,6 +15860,7 @@ package android.media.tv { } public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns { + method public static final java.lang.String getVideoResolution(java.lang.String); field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; field public static final java.lang.String COLUMN_DESCRIPTION = "description"; field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name"; @@ -15860,6 +15874,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id"; field public static final java.lang.String COLUMN_TYPE = "type"; field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel"; field public static final android.net.Uri CONTENT_URI; @@ -15891,6 +15906,22 @@ package android.media.tv { field public static final int TYPE_SECAM = 3; // 0x3 field public static final int TYPE_S_DMB = 393472; // 0x60100 field public static final int TYPE_T_DMB = 393216; // 0x60000 + field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I"; + field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P"; + field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P"; + field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P"; + field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P"; + field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P"; + field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I"; + field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P"; + field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I"; + field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P"; + field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P"; + field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED"; + field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD"; + field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD"; + field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD"; + field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD"; } public static final class TvContract.Channels.Logo { @@ -15911,6 +15942,8 @@ package android.media.tv { field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; field public static final java.lang.String COLUMN_TITLE = "title"; field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program"; field public static final android.net.Uri CONTENT_URI; @@ -15919,17 +15952,17 @@ package android.media.tv { public static final class TvContract.Programs.Genres { method public static java.lang.String[] decode(java.lang.String); method public static java.lang.String encode(java.lang.String...); - field public static final java.lang.String ANIMAL_WILDLIFE = "Animal/Wildlife"; - field public static final java.lang.String COMEDY = "Comedy"; - field public static final java.lang.String DRAMA = "Drama"; - field public static final java.lang.String EDUCATION = "Education"; - field public static final java.lang.String FAMILY_KIDS = "Family/Kids"; - field public static final java.lang.String GAMING = "Gaming"; - field public static final java.lang.String MOVIES = "Movies"; - field public static final java.lang.String NEWS = "News"; - field public static final java.lang.String SHOPPING = "Shopping"; - field public static final java.lang.String SPORTS = "Sports"; - field public static final java.lang.String TRAVEL = "Travel"; + field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE"; + field public static final java.lang.String COMEDY = "COMEDY"; + field public static final java.lang.String DRAMA = "DRAMA"; + field public static final java.lang.String EDUCATION = "EDUCATION"; + field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS"; + field public static final java.lang.String GAMING = "GAMING"; + field public static final java.lang.String MOVIES = "MOVIES"; + field public static final java.lang.String NEWS = "NEWS"; + field public static final java.lang.String SHOPPING = "SHOPPING"; + field public static final java.lang.String SPORTS = "SPORTS"; + field public static final java.lang.String TRAVEL = "TRAVEL"; } public final class TvInputInfo implements android.os.Parcelable { @@ -21560,6 +21593,7 @@ package android.os { public class UserManager { method public android.os.Bundle getApplicationRestrictions(java.lang.String); method public android.graphics.drawable.Drawable getBadgedDrawableForUser(android.graphics.drawable.Drawable, android.os.UserHandle); + method public java.lang.String getBadgedLabelForUser(java.lang.String, android.os.UserHandle); method public long getSerialNumberForUser(android.os.UserHandle); method public int getUserCount(); method public android.os.UserHandle getUserForSerialNumber(long); @@ -31318,6 +31352,7 @@ package android.view { } public final class Display { + method public long getAppVsyncOffsetNanos(); method public void getCurrentSizeRange(android.graphics.Point, android.graphics.Point); method public int getDisplayId(); method public int getFlags(); @@ -31326,6 +31361,7 @@ package android.view { method public java.lang.String getName(); method public deprecated int getOrientation(); method public deprecated int getPixelFormat(); + method public long getPresentationDeadlineNanos(); method public void getRealMetrics(android.util.DisplayMetrics); method public void getRealSize(android.graphics.Point); method public void getRectSize(android.graphics.Rect); @@ -32558,6 +32594,7 @@ package android.view { method public void dispatchWindowSystemUiVisiblityChanged(int); method public void dispatchWindowVisibilityChanged(int); method public void draw(android.graphics.Canvas); + method public void drawableHotspotChanged(float, float); method protected void drawableStateChanged(); method public android.view.View findFocus(); method public final android.view.View findViewById(int); diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp index ae35f7b..593a197 100644 --- a/cmds/idmap/create.cpp +++ b/cmds/idmap/create.cpp @@ -105,7 +105,7 @@ fail: uint32_t cached_target_crc, cached_overlay_crc; String8 cached_target_path, cached_overlay_path; - if (!ResTable::getIdmapInfo(buf, N, &cached_target_crc, &cached_overlay_crc, + if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc, &cached_target_path, &cached_overlay_path)) { return true; } diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp index 46c0edc..90cfa2c 100644 --- a/cmds/idmap/idmap.cpp +++ b/cmds/idmap/idmap.cpp @@ -66,26 +66,32 @@ EXAMPLES \n\ Display an idmap file: \n\ \n\ $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\ - SECTION ENTRY VALUE OFFSET COMMENT \n\ - IDMAP HEADER magic 0x706d6469 0x0 \n\ - base crc 0x484aa77f 0x1 \n\ - overlay crc 0x03c66fa5 0x2 \n\ - base path .......... 0x03-0x42 /system/app/target.apk \n\ - overlay path .......... 0x43-0x82 /vendor/overlay/overlay.apk \n\ - DATA HEADER types count 0x00000003 0x83 \n\ - padding 0x00000000 0x84 \n\ - type offset 0x00000004 0x85 absolute offset 0x87, xml \n\ - type offset 0x00000007 0x86 absolute offset 0x8a, string \n\ - DATA BLOCK entry count 0x00000001 0x87 \n\ - entry offset 0x00000000 0x88 \n\ - entry 0x7f020000 0x89 xml/integer \n\ - DATA BLOCK entry count 0x00000002 0x8a \n\ - entry offset 0x00000000 0x8b \n\ - entry 0x7f030000 0x8c string/str \n\ - entry 0x7f030001 0x8d string/str2 \n\ + SECTION ENTRY VALUE COMMENT \n\ + IDMAP HEADER magic 0x706d6469 \n\ + base crc 0xb65a383f \n\ + overlay crc 0x7b9675e8 \n\ + base path .......... /path/to/target.apk \n\ + overlay path .......... /path/to/overlay.apk \n\ + DATA HEADER target pkg 0x0000007f \n\ + types count 0x00000003 \n\ + DATA BLOCK target type 0x00000002 \n\ + overlay type 0x00000002 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 drawable/drawable \n\ + DATA BLOCK target type 0x00000003 \n\ + overlay type 0x00000003 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 xml/integer \n\ + DATA BLOCK target type 0x00000004 \n\ + overlay type 0x00000004 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 raw/lorem_ipsum \n\ \n\ In this example, the overlay package provides three alternative resource values:\n\ - xml/integer, string/str and string/str2.\n\ + drawable/drawable, xml/integer, and raw/lorem_ipsum \n\ \n\ NOTES \n\ This tool and its expected invocation from installd is modelled on dexopt."; diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp index a59f5d3..b9ac8a5 100644 --- a/cmds/idmap/inspect.cpp +++ b/cmds/idmap/inspect.cpp @@ -10,92 +10,108 @@ using namespace android; -#define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0) - namespace { - static const uint32_t IDMAP_MAGIC = 0x706d6469; + static const uint32_t IDMAP_MAGIC = 0x504D4449; static const size_t PATH_LENGTH = 256; - static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t))); void printe(const char *fmt, ...); class IdmapBuffer { private: - char *buf_; + const char* buf_; size_t len_; - mutable size_t pos_; + size_t pos_; public: - IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {} + IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {} ~IdmapBuffer() { if (buf_ != MAP_FAILED) { - munmap(buf_, len_); + munmap(const_cast<char*>(buf_), len_); } } - int init(const char *idmap_path) - { + status_t init(const char *idmap_path) { struct stat st; int fd; if (stat(idmap_path, &st) < 0) { printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno)); - return -1; + return UNKNOWN_ERROR; } len_ = st.st_size; if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) { printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno)); - return -1; + return UNKNOWN_ERROR; } - if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { close(fd); printe("failed to mmap idmap: %s\n", strerror(errno)); - return -1; + return UNKNOWN_ERROR; } close(fd); - return 0; + return NO_ERROR; } - int next(uint32_t *i, uint32_t *offset) const - { + status_t nextUint32(uint32_t* i) { if (!buf_) { printe("failed to read next uint32_t: buffer not initialized\n"); - return -1; + return UNKNOWN_ERROR; } - if (pos_ + 4 > len_) { + + if (pos_ + sizeof(uint32_t) > len_) { printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n", pos_); - return -1; + return UNKNOWN_ERROR; + } + + if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) { + printe("failed to read next uint32_t: not aligned on 4-byte boundary\n"); + return UNKNOWN_ERROR; } - *offset = pos_ / sizeof(uint32_t); - char a = buf_[pos_++]; - char b = buf_[pos_++]; - char c = buf_[pos_++]; - char d = buf_[pos_++]; - *i = (d << 24) | (c << 16) | (b << 8) | a; - return 0; + + *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_)); + pos_ += sizeof(uint32_t); + return NO_ERROR; } - int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const - { + status_t nextUint16(uint16_t* i) { + if (!buf_) { + printe("failed to read next uint16_t: buffer not initialized\n"); + return UNKNOWN_ERROR; + } + + if (pos_ + sizeof(uint16_t) > len_) { + printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n", + pos_); + return UNKNOWN_ERROR; + } + + if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) { + printe("failed to read next uint32_t: not aligned on 2-byte boundary\n"); + return UNKNOWN_ERROR; + } + + *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_)); + pos_ += sizeof(uint16_t); + return NO_ERROR; + } + + status_t nextPath(char *b) { if (!buf_) { printe("failed to read next path: buffer not initialized\n"); - return -1; + return UNKNOWN_ERROR; } if (pos_ + PATH_LENGTH > len_) { printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_); - return -1; + return UNKNOWN_ERROR; } memcpy(b, buf_ + pos_, PATH_LENGTH); - *offset_start = pos_ / sizeof(uint32_t); pos_ += PATH_LENGTH; - *offset_end = pos_ / sizeof(uint32_t) - 1; - return 0; + return NO_ERROR; } }; - void printe(const char *fmt, ...) - { + void printe(const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -104,44 +120,37 @@ namespace { va_end(ap); } - void print_header() - { - printf("SECTION ENTRY VALUE OFFSET COMMENT\n"); + void print_header() { + printf("SECTION ENTRY VALUE COMMENT\n"); } - void print(const char *section, const char *subsection, uint32_t value, uint32_t offset, - const char *fmt, ...) - { + void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printf("%-12s %-12s 0x%08x 0x%-4x ", section, subsection, value, offset); + printf("%-12s %-12s 0x%08x ", section, subsection, value); vprintf(fmt, ap); printf("\n"); va_end(ap); } - void print_path(const char *section, const char *subsection, uint32_t offset_start, - uint32_t offset_end, const char *fmt, ...) - { + void print_path(const char *section, const char *subsection, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start, - offset_end); + printf("%-12s %-12s .......... ", section, subsection); vprintf(fmt, ap); printf("\n"); va_end(ap); } - int resource_metadata(const AssetManager& am, uint32_t res_id, - String8 *package, String8 *type, String8 *name) - { + status_t resource_metadata(const AssetManager& am, uint32_t res_id, + String8 *package, String8 *type, String8 *name) { const ResTable& rt = am.getResources(); struct ResTable::resource_name data; if (!rt.getResourceName(res_id, false, &data)) { printe("failed to get resource name id=0x%08x\n", res_id); - return -1; + return UNKNOWN_ERROR; } if (package) { *package = String8(String16(data.package, data.packageLen)); @@ -152,140 +161,150 @@ namespace { if (name) { *name = String8(String16(data.name, data.nameLen)); } - return 0; - } - - int package_id(const AssetManager& am) - { - return (am.getResources().getBasePackageId(0)) << 24; + return NO_ERROR; } - int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am) - { - uint32_t i, o, e; + status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) { + uint32_t i; char path[PATH_LENGTH]; - NEXT(buf, i, o); + status_t err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + if (i != IDMAP_MAGIC) { printe("not an idmap file: actual magic constant 0x%08x does not match expected magic " "constant 0x%08x\n", i, IDMAP_MAGIC); - return -1; + return UNKNOWN_ERROR; } + print_header(); - print("IDMAP HEADER", "magic", i, o, ""); + print("IDMAP HEADER", "magic", i, ""); + + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "version", i, ""); - NEXT(buf, i, o); - print("", "base crc", i, o, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "base crc", i, ""); - NEXT(buf, i, o); - print("", "overlay crc", i, o, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "overlay crc", i, ""); - if (buf.nextPath(path, &o, &e) < 0) { + err = buf.nextPath(path); + if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath - return -1; + return err; } - print_path("", "base path", o, e, "%s", path); + print_path("", "base path", "%s", path); + if (!am.addAssetPath(String8(path), NULL)) { printe("failed to add '%s' as asset path\n", path); - return -1; + return UNKNOWN_ERROR; } - if (buf.nextPath(path, &o, &e) < 0) { + err = buf.nextPath(path); + if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath - return -1; + return err; } - print_path("", "overlay path", o, e, "%s", path); + print_path("", "overlay path", "%s", path); - return 0; + return NO_ERROR; } - int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector<uint32_t>& types) - { - uint32_t i, o; - const uint32_t numeric_package = package_id(am); + status_t parse_data(IdmapBuffer& buf, const AssetManager& am) { + const uint32_t packageId = am.getResources().getBasePackageId(0); - NEXT(buf, i, o); - print("DATA HEADER", "types count", i, o, ""); - const uint32_t N = i; + uint16_t data16; + status_t err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), ""); - for (uint32_t j = 0; j < N; ++j) { - NEXT(buf, i, o); - if (i == 0) { - print("", "padding", i, o, ""); - } else { - String8 type; - const uint32_t numeric_type = (j + 1) << 16; - const uint32_t res_id = numeric_package | numeric_type; - if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) { - // printe done from resource_metadata - return -1; - } - print("", "type offset", i, o, "absolute offset 0x%02x, %s", - i + IDMAP_HEADER_SIZE, type.string()); - types.add(numeric_type); - } + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; } + print("", "types count", static_cast<uint32_t>(data16), ""); - return 0; - } + uint32_t typeCount = static_cast<uint32_t>(data16); + while (typeCount > 0) { + typeCount--; + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t targetTypeId = static_cast<uint32_t>(data16); + print("DATA BLOCK", "target type", targetTypeId, ""); - int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type) - { - uint32_t i, o, n, id_offset; - const uint32_t numeric_package = package_id(am); - - NEXT(buf, i, o); - print("DATA BLOCK", "entry count", i, o, ""); - n = i; - - NEXT(buf, i, o); - print("", "entry offset", i, o, ""); - id_offset = i; - - for ( ; n > 0; --n) { - String8 type, name; - - NEXT(buf, i, o); - if (i == 0) { - print("", "padding", i, o, ""); - } else { - uint32_t res_id = numeric_package | numeric_type | id_offset; - if (resource_metadata(am, res_id, NULL, &type, &name) < 0) { - // printe done from resource_metadata - return -1; + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + print("", "overlay type", static_cast<uint32_t>(data16), ""); + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t entryCount = static_cast<uint32_t>(data16); + print("", "entry count", entryCount, ""); + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t entryOffset = static_cast<uint32_t>(data16); + print("", "entry offset", entryOffset, ""); + + for (uint32_t i = 0; i < entryCount; i++) { + uint32_t data32; + err = buf.nextUint32(&data32); + if (err != NO_ERROR) { + return err; } - print("", "entry", i, o, "%s/%s", type.string(), name.string()); + + uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i); + String8 type; + String8 name; + err = resource_metadata(am, resID, NULL, &type, &name); + if (err != NO_ERROR) { + return err; + } + print("", "entry", data32, "%s/%s", type.string(), name.string()); } - ++id_offset; } - return 0; + return NO_ERROR; } } -int idmap_inspect(const char *idmap_path) -{ +int idmap_inspect(const char *idmap_path) { IdmapBuffer buf; if (buf.init(idmap_path) < 0) { // printe done from IdmapBuffer::init return EXIT_FAILURE; } AssetManager am; - if (parse_idmap_header(buf, am) < 0) { + if (parse_idmap_header(buf, am) != NO_ERROR) { // printe done from parse_idmap_header return EXIT_FAILURE; } - Vector<uint32_t> types; - if (parse_data_header(buf, am, types) < 0) { + if (parse_data(buf, am) != NO_ERROR) { // printe done from parse_data_header return EXIT_FAILURE; } - const size_t N = types.size(); - for (size_t i = 0; i < N; ++i) { - if (parse_data_block(buf, am, types.itemAt(i)) < 0) { - // printe done from parse_data_block - return EXIT_FAILURE; - } - } return EXIT_SUCCESS; } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 90615d3..9132883 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5323,13 +5323,15 @@ public class Activity extends ContextThemeWrapper * drawn and it is safe to make this Activity translucent again. * @param options activity options delivered to the activity below this one. The options * are retrieved using {@link #getActivityOptions}. + * @return <code>true</code> if Window was opaque and will become translucent or + * <code>false</code> if window was translucent and no change needed to be made. * * @see #convertFromTranslucent() * @see TranslucentConversionListener * * @hide */ - public void convertToTranslucent(TranslucentConversionListener callback, + public boolean convertToTranslucent(TranslucentConversionListener callback, ActivityOptions options) { boolean drawComplete; try { @@ -5346,6 +5348,7 @@ public class Activity extends ContextThemeWrapper // Window is already translucent. mTranslucentCallback.onTranslucentConversionComplete(drawComplete); } + return mChangeCanvasToTranslucent; } /** @hide */ diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index de0396e..5e17e1a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1457,10 +1457,10 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId) { + public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, + int flags) { try { - mPM.addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); + mPM.addCrossProfileIntentFilter(filter, sourceUserId, targetUserId, flags); } catch (RemoteException e) { // Should never happen! } @@ -1470,15 +1470,6 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId, - int targetUserId) { - addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); - } - - /** - * @hide - */ - @Override public void clearCrossProfileIntentFilters(int sourceUserId) { try { mPM.clearCrossProfileIntentFilters(sourceUserId); @@ -1487,14 +1478,6 @@ final class ApplicationPackageManager extends PackageManager { } } - /** - * @hide - */ - @Override - public void clearForwardingIntentFilters(int sourceUserId) { - clearCrossProfileIntentFilters(sourceUserId); - } - private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index ef4099f..5998d7a 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -1184,6 +1184,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInt(level); mRemote.transact(SCHEDULE_TRIM_MEMORY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); + data.recycle(); } public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 365cc8e..1d7a0ec 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -55,6 +55,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private boolean mIsExitTransitionComplete; private boolean mIsReadyForTransition; private Bundle mSharedElementsBundle; + private boolean mWasOpaque; public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, ArrayList<String> sharedElementNames, boolean isReturning) { @@ -191,7 +192,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { protected void prepareEnter() { mActivity.overridePendingTransition(0, 0); if (!mIsReturning) { - mActivity.convertToTranslucent(null, null); + mWasOpaque = mActivity.convertToTranslucent(null, null); Drawable background = getDecor().getBackground(); if (background != null) { getWindow().setBackgroundDrawable(null); @@ -376,7 +377,9 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private void makeOpaque() { if (!mHasStopped && mActivity != null) { - mActivity.convertFromTranslucent(); + if (mWasOpaque) { + mActivity.convertFromTranslucent(); + } mActivity = null; } } diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 45a2625..ca40436 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -166,6 +166,40 @@ public class DeviceAdminReceiver extends BroadcastReceiver { = "android.app.action.ACTION_PASSWORD_EXPIRING"; /** + * Action sent to a device administrator to notify that the device is entering + * or exiting lock task mode from an authorized package. The extra + * {@link #EXTRA_LOCK_TASK_ENTERING} will describe whether entering or exiting + * the mode. If entering, the extra {@link #EXTRA_LOCK_TASK_PACKAGE} will describe + * the authorized package using lock task mode. + * + * @see DevicePolicyManager#isLockTaskPermitted + * + * <p>The calling device admin must be the device owner or profile + * owner to receive this broadcast. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LOCK_TASK_CHANGED + = "android.app.action.ACTION_LOCK_TASK_CHANGED"; + + /** + * A boolean describing whether the device is currently entering or exiting + * lock task mode. + * + * @see #ACTION_LOCK_TASK_CHANGED + */ + public static final String EXTRA_LOCK_TASK_ENTERING = + "android.app.extra.LOCK_TASK_ENTERING"; + + /** + * A boolean describing whether the device is currently entering or exiting + * lock task mode. + * + * @see #ACTION_LOCK_TASK_CHANGED + */ + public static final String EXTRA_LOCK_TASK_PACKAGE = + "android.app.extra.LOCK_TASK_PACKAGE"; + + /** * Broadcast Action: This broadcast is sent to indicate that provisioning of a managed profile * or managed device has completed successfully. * @@ -341,6 +375,19 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } /** + * Called when a device is entering or exiting lock task mode by a package + * authorized by {@link DevicePolicyManager#isLockTaskPermitted(String)} + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param isEnteringLockTask Whether the device is entering or exiting lock task mode. + * @param pkg If entering, the authorized package using lock task mode, otherwise null. + */ + public void onLockTaskModeChanged(Context context, Intent intent, boolean isEnteringLockTask, + String pkg) { + } + + /** * Intercept standard device administrator broadcasts. Implementations * should not override this method; it is better to implement the * convenience callbacks for each action. @@ -369,6 +416,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver { onPasswordExpiring(context, intent); } else if (ACTION_PROFILE_PROVISIONING_COMPLETE.equals(action)) { onProfileProvisioningComplete(context, intent); + } else if (ACTION_LOCK_TASK_CHANGED.equals(action)) { + boolean isEntering = intent.getBooleanExtra(EXTRA_LOCK_TASK_ENTERING, false); + String pkg = intent.getStringExtra(EXTRA_LOCK_TASK_PACKAGE); + onLockTaskModeChanged(context, intent, isEntering, pkg); } } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ae1a4e7..df6be8b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1507,12 +1507,11 @@ public class DevicePolicyManager { * * @return false if the certBuffer cannot be parsed or installation is * interrupted, otherwise true - * @hide */ - public boolean installCaCert(byte[] certBuffer) { + public boolean installCaCert(ComponentName who, byte[] certBuffer) { if (mService != null) { try { - return mService.installCaCert(certBuffer); + return mService.installCaCert(who, certBuffer); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1522,13 +1521,14 @@ public class DevicePolicyManager { /** * Uninstalls the given certificate from the list of User CAs, if present. - * - * @hide */ - public void uninstallCaCert(byte[] certBuffer) { + public void uninstallCaCert(ComponentName who, byte[] certBuffer) { if (mService != null) { try { - mService.uninstallCaCert(certBuffer); + final String alias = getCaCertAlias(certBuffer); + mService.uninstallCaCert(who, alias); + } catch (CertificateException e) { + Log.w(TAG, "Unable to parse certificate", e); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1537,10 +1537,8 @@ public class DevicePolicyManager { /** * Returns whether there are any user-installed CA certificates. - * - * @hide */ - public static boolean hasAnyCaCertsInstalled() { + public boolean hasAnyCaCertsInstalled() { TrustedCertificateStore certStore = new TrustedCertificateStore(); Set<String> aliases = certStore.userAliases(); return aliases != null && !aliases.isEmpty(); @@ -1548,18 +1546,10 @@ public class DevicePolicyManager { /** * Returns whether this certificate has been installed as a User CA. - * - * @hide */ public boolean hasCaCertInstalled(byte[] certBuffer) { - TrustedCertificateStore certStore = new TrustedCertificateStore(); - String alias; - byte[] pemCert; try { - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - X509Certificate cert = (X509Certificate) certFactory.generateCertificate( - new ByteArrayInputStream(certBuffer)); - return certStore.getCertificateAlias(cert) != null; + return getCaCertAlias(certBuffer) != null; } catch (CertificateException ce) { Log.w(TAG, "Could not parse certificate", ce); } @@ -1567,6 +1557,17 @@ public class DevicePolicyManager { } /** + * Returns the alias of a given CA certificate in the certificate store, or null if it + * doesn't exist. + */ + private static String getCaCertAlias(byte[] certBuffer) throws CertificateException { + final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + final X509Certificate cert = (X509Certificate) certFactory.generateCertificate( + new ByteArrayInputStream(certBuffer)); + return new TrustedCertificateStore().getCertificateAlias(cert); + } + + /** * Called by an application that is administering the device to disable all cameras * on the device. After setting this, no applications will be able to access any cameras * on the device. @@ -1840,11 +1841,13 @@ public class DevicePolicyManager { * This function should be used cautiously as once it is called it cannot * be undone. The device owner can only be set as a part of device setup * before setup completes. + * + * @param packageName The package name of the device owner. */ - public void clearDeviceOwnerApp() { + public void clearDeviceOwnerApp(String packageName) { if (mService != null) { try { - mService.clearDeviceOwner(mContext.getPackageName()); + mService.clearDeviceOwner(packageName); } catch (RemoteException re) { Log.w(TAG, "Failed to clear device owner"); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 8272c07..5333ea6 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -115,8 +115,8 @@ interface IDevicePolicyManager { String getProfileOwnerName(int userHandle); void setProfileEnabled(in ComponentName who); - boolean installCaCert(in byte[] certBuffer); - void uninstallCaCert(in byte[] certBuffer); + boolean installCaCert(in ComponentName admin, in byte[] certBuffer); + void uninstallCaCert(in ComponentName admin, in String alias); void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity); void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName); @@ -151,4 +151,6 @@ interface IDevicePolicyManager { void setMasterVolumeMuted(in ComponentName admin, boolean on); boolean isMasterVolumeMuted(in ComponentName admin); + + void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userId); } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 06f4019..be4e864 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -325,6 +325,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_IS_GAME = 1<<25; /** + * Value for {@link #flags}: {@code true} if the application asks that only + * full-data streaming backups of its data be performed even though it defines + * a {@link android.app.backup.BackupAgent BackupAgent}, which normally + * indicates that the app will manage its backed-up data via incremental + * key/value updates. + */ + public static final int FLAG_FULL_BACKUP_ONLY = 1<<26; + + /** * Value for {@link #flags}: set to {@code true} if the application * is permitted to hold privileged permissions. * diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 70668e1..00e7918 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -248,8 +248,8 @@ interface IPackageManager { void clearPackagePersistentPreferredActivities(String packageName, int userId); - void addCrossProfileIntentFilter(in IntentFilter filter, boolean removable, int sourceUserId, - int targetUserId); + void addCrossProfileIntentFilter(in IntentFilter intentFilter, int sourceUserId, int targetUserId, + int flags); void clearCrossProfileIntentFilters(int sourceUserId); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 8d9b8d9..b5ceebe 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -196,6 +196,22 @@ public abstract class PackageManager { */ public static final int MATCH_DEFAULT_ONLY = 0x00010000; + /** + * Flag for {@link addCrossProfileIntentFilter}: if the cross-profile intent has been set by the + * profile owner. + * @hide + */ + public static final int SET_BY_PROFILE_OWNER= 0x00000001; + + /** + * Flag for {@link addCrossProfileIntentFilter}: if this flag is set: + * when resolving an intent that matches the {@link CrossProfileIntentFilter}, the current + * profile will be skipped. + * Only activities in the target user can respond to the intent. + * @hide + */ + public static final int SKIP_CURRENT_PROFILE = 0x00000002; + /** @hide */ @IntDef({PERMISSION_GRANTED, PERMISSION_DENIED}) @Retention(RetentionPolicy.SOURCE) @@ -2870,15 +2886,12 @@ public abstract class PackageManager { * */ public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) { - PackageParser packageParser = new PackageParser(archiveFilePath); - DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - final File sourceFile = new File(archiveFilePath); + final PackageParser parser = new PackageParser(); + final File apkFile = new File(archiveFilePath); try { - PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, - 0); + PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0); if ((flags & GET_SIGNATURES) != 0) { - packageParser.collectCertificates(pkg, 0); + parser.collectCertificates(pkg, 0); } PackageUserState state = new PackageUserState(); return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state); @@ -3586,30 +3599,14 @@ public abstract class PackageManager { * {@link CrossProfileIntentFilter} * @hide */ - public abstract void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId); + public abstract void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, + int targetUserId, int flags); /** - * @hide - * @deprecated - * TODO: remove it as soon as the code of ManagedProvisionning is updated - */ - public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId); - - /** - * Clearing removable {@link CrossProfileIntentFilter}s which have the specified user as their - * source + * Clearing {@link CrossProfileIntentFilter}s which have the specified user as their + * source, and have been set by the profile owner * @param sourceUserId - * be cleared. * @hide */ public abstract void clearCrossProfileIntentFilters(int sourceUserId); - - /** - * @hide - * @deprecated - * TODO: remove it as soon as the code of ManagedProvisionning is updated - */ - public abstract void clearForwardingIntentFilters(int sourceUserId); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 618c2bd..546f3a5 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -16,9 +16,14 @@ package android.content.pm; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; import android.content.ComponentName; import android.content.Intent; @@ -33,6 +38,8 @@ import android.os.Bundle; import android.os.PatternMatcher; import android.os.UserHandle; import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AttributeSet; import android.util.Base64; import android.util.DisplayMetrics; @@ -41,12 +48,18 @@ import android.util.Pair; import android.util.Slog; import android.util.TypedValue; -import java.io.BufferedInputStream; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.XmlUtils; + +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.lang.ref.WeakReference; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; @@ -58,21 +71,19 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.jar.StrictJarFile; import java.util.zip.ZipEntry; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.XmlUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - /** * Package archive parsing * @@ -153,17 +164,21 @@ public class PackageParser { android.os.Build.VERSION_CODES.JELLY_BEAN) }; + /** + * @deprecated callers should move to explicitly passing around source path. + */ + @Deprecated private String mArchiveSourcePath; + private String[] mSeparateProcesses; private boolean mOnlyCoreApps; + private DisplayMetrics mMetrics; + private static final int SDK_VERSION = Build.VERSION.SDK_INT; private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; private int mParseError = PackageManager.INSTALL_SUCCEEDED; - private static final Object mSync = new Object(); - private static WeakReference<byte[]> mReadBuffer; - private static boolean sCompatibilityModeEnabled = true; private static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; @@ -247,12 +262,9 @@ public class PackageParser { private static final String TAG = "PackageParser"; - public PackageParser(String archiveSourcePath) { - mArchiveSourcePath = archiveSourcePath; - } - - public PackageParser(File archiveSource) { - this(archiveSource.getAbsolutePath()); + public PackageParser() { + mMetrics = new DisplayMetrics(); + mMetrics.setToDefaults(); } public void setSeparateProcesses(String[] procs) { @@ -263,6 +275,10 @@ public class PackageParser { mOnlyCoreApps = onlyCoreApps; } + public void setDisplayMetrics(DisplayMetrics metrics) { + mMetrics = metrics; + } + private static final boolean isPackageFilename(File file) { return isPackageFilename(file.getName()); } @@ -480,23 +496,21 @@ public class PackageParser { return pi; } - private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je, - byte[] readBuffer) { + private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry) + throws PackageParserException { + InputStream is = null; try { // We must read the stream for the JarEntry to retrieve // its certificates. - InputStream is = new BufferedInputStream(jarFile.getInputStream(je)); - while (is.read(readBuffer, 0, readBuffer.length) != -1) { - // not using - } - is.close(); - return je != null ? jarFile.getCertificateChains(je) : null; - } catch (IOException e) { - Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e); - } catch (RuntimeException e) { - Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e); + is = jarFile.getInputStream(entry); + readFullyIgnoringContents(is); + return jarFile.getCertificateChains(entry); + } catch (IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed reading " + entry.getName() + " in " + jarFile, e); + } finally { + IoUtils.closeQuietly(is); } - return null; } public final static int PARSE_IS_SYSTEM = 1<<0; @@ -508,67 +522,116 @@ public class PackageParser { public final static int PARSE_IS_SYSTEM_DIR = 1<<6; public final static int PARSE_IS_PRIVILEGED = 1<<7; public final static int PARSE_GET_SIGNATURES = 1<<8; + public final static int PARSE_TRUSTED_OVERLAY = 1<<9; + + private static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); /** - * Parse all APK files under the given directory as a single package. + * Used to sort a set of APKs based on their split names, always placing the + * base APK (with {@code null} split name) first. */ - public Package parseSplitPackage(File apkDir, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { + private static class SplitNameComparator implements Comparator<String> { + @Override + public int compare(String lhs, String rhs) { + if (lhs == null) { + return -1; + } else if (rhs == null) { + return 1; + } else { + return lhs.compareTo(rhs); + } + } + } + + /** + * Parse all APKs contained in the given directory, treating them as a + * single package. This also performs sanity checking, such as requiring + * identical package name and version codes, a single base APK, and unique + * split names. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(Package, int)}. + */ + public Package parseSplitPackage(File apkDir, int flags) throws PackageParserException { final File[] files = apkDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split"); } - File baseFile = null; + String packageName = null; + int versionCode = 0; + + final ArrayMap<String, File> apks = new ArrayMap<>(); for (File file : files) { if (file.isFile() && isPackageFilename(file)) { - ApkLite lite = parseApkLite(file.getAbsolutePath(), 0); - if (lite == null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + file); + final ApkLite lite = parseApkLite(file, 0); + + // Assert that all package names and version codes are + // consistent with the first one we encounter. + if (packageName == null) { + packageName = lite.packageName; + versionCode = lite.versionCode; + } else { + if (!packageName.equals(lite.packageName)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Inconsistent package " + lite.packageName + " in " + file + + "; expected " + packageName); + } + if (versionCode != lite.versionCode) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Inconsistent version " + lite.versionCode + " in " + file + + "; expected " + versionCode); + } } - if (TextUtils.isEmpty(lite.splitName)) { - baseFile = file; - break; + // Assert that each split is defined only once + if (apks.put(lite.splitName, file) != null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Split name " + lite.splitName + + " defined more than once; most recent was " + file); } } } + final File baseFile = apks.remove(null); if (baseFile == null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Missing base APK in " + apkDir); } - final Package pkg = parseBaseApk(baseFile, metrics, flags, trustedOverlay); + // Always apply deterministic ordering based on splitName + final int size = apks.size(); + + final String[] splitNames = apks.keySet().toArray(new String[size]); + Arrays.sort(splitNames, sSplitNameComparator); + + final File[] splitFiles = new File[size]; + for (int i = 0; i < size; i++) { + splitFiles[i] = apks.get(splitNames[i]); + } + + final Package pkg = parseBaseApk(baseFile, flags); if (pkg == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse base APK: " + baseFile); } - for (File file : files) { - if (file.isFile() && isPackageFilename(file) && !file.equals(baseFile)) { - parseSplitApk(pkg, file, metrics, flags, trustedOverlay); - } - } - - // Always use a well-defined sort order - if (pkg.splitCodePaths != null) { - Arrays.sort(pkg.splitCodePaths); + for (File splitFile : splitFiles) { + parseSplitApk(pkg, splitFile, flags); } return pkg; } - public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags) - throws PackageParserException { - return parseMonolithicPackage(apkFile, metrics, flags, false); - } - - public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { - final Package pkg = parseBaseApk(apkFile, metrics, flags, trustedOverlay); + /** + * Parse the given APK file, treating it as as a single monolithic package. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(Package, int)}. + */ + public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { + final Package pkg = parseBaseApk(apkFile, flags); if (pkg != null) { return pkg; } else { @@ -576,13 +639,15 @@ public class PackageParser { } } - private Package parseBaseApk(File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) { + private Package parseBaseApk(File apkFile, int flags) { + final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0; + mParseError = PackageManager.INSTALL_SUCCEEDED; + final String apkPath = apkFile.getAbsolutePath(); mArchiveSourcePath = apkFile.getAbsolutePath(); if (!apkFile.isFile()) { - Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath); + Slog.w(TAG, "Skipping dir: " + apkPath); mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } @@ -591,14 +656,14 @@ public class PackageParser { if ((flags&PARSE_IS_SYSTEM) == 0) { // We expect to have non-.apk files in the system dir, // so don't warn about them. - Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath); + Slog.w(TAG, "Skipping non-package file: " + apkPath); } mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } if (DEBUG_JAR) - Slog.d(TAG, "Scanning package: " + mArchiveSourcePath); + Slog.d(TAG, "Scanning package: " + apkPath); XmlResourceParser parser = null; AssetManager assmgr = null; @@ -606,19 +671,18 @@ public class PackageParser { boolean assetError = true; try { assmgr = new AssetManager(); - int cookie = assmgr.addAssetPath(mArchiveSourcePath); + int cookie = assmgr.addAssetPath(apkPath); if (cookie != 0) { - res = new Resources(assmgr, metrics, null); + res = new Resources(assmgr, mMetrics, null); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); assetError = false; } else { - Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath); + Slog.w(TAG, "Failed adding asset path:" + apkPath); } } catch (Exception e) { - Slog.w(TAG, "Unable to read AndroidManifest.xml of " - + mArchiveSourcePath, e); + Slog.w(TAG, "Unable to read AndroidManifest.xml of " + apkPath, e); } if (assetError) { if (assmgr != null) assmgr.close(); @@ -641,9 +705,9 @@ public class PackageParser { // just means to skip this app so don't make a fuss about it. if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) { if (errorException != null) { - Slog.w(TAG, mArchiveSourcePath, errorException); + Slog.w(TAG, apkPath, errorException); } else { - Slog.w(TAG, mArchiveSourcePath + " (at " + Slog.w(TAG, apkPath + " (at " + parser.getPositionDescription() + "): " + errorText[0]); } @@ -659,14 +723,16 @@ public class PackageParser { parser.close(); assmgr.close(); - pkg.codePath = mArchiveSourcePath; + pkg.codePath = apkPath; pkg.mSignatures = null; return pkg; } - private void parseSplitApk(Package pkg, File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { + private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + mArchiveSourcePath = apkFile.getAbsolutePath(); + // TODO: expand split APK parsing pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths, apkFile.getAbsolutePath()); @@ -678,8 +744,9 @@ public class PackageParser { * {@code AndroidManifest.xml}, {@code true} is returned. */ public void collectManifestDigest(Package pkg) throws PackageParserException { + // TODO: extend to gather digest for split APKs try { - final StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath); + final StrictJarFile jarFile = new StrictJarFile(pkg.codePath); try { final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { @@ -688,186 +755,127 @@ public class PackageParser { } finally { jarFile.close(); } - } catch (IOException e) { + } catch (IOException | RuntimeException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Failed to collect manifest digest"); } } + /** + * Collect certificates from all the APKs described in the given package, + * populating {@link Package#mSignatures}. This also asserts that all APK + * contents are signed correctly and consistently. + */ public void collectCertificates(Package pkg, int flags) throws PackageParserException { - if (!collectCertificatesInternal(pkg, flags)) { - throw new PackageParserException(mParseError, "Failed to collect certificates"); - } - } - - private boolean collectCertificatesInternal(Package pkg, int flags) { + pkg.mCertificates = null; pkg.mSignatures = null; + pkg.mSigningKeys = null; - WeakReference<byte[]> readBufferRef; - byte[] readBuffer = null; - synchronized (mSync) { - readBufferRef = mReadBuffer; - if (readBufferRef != null) { - mReadBuffer = null; - readBuffer = readBufferRef.get(); - } - if (readBuffer == null) { - readBuffer = new byte[8192]; - readBufferRef = new WeakReference<byte[]>(readBuffer); + collectCertificates(pkg, new File(pkg.codePath), flags); + + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + for (String splitCodePath : pkg.splitCodePaths) { + collectCertificates(pkg, new File(splitCodePath), flags); } } + } + + private static void collectCertificates(Package pkg, File apkFile, int flags) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + StrictJarFile jarFile = null; try { - StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath); - - Certificate[][] certs = null; - - if ((flags&PARSE_IS_SYSTEM) != 0) { - // If this package comes from the system image, then we - // can trust it... we'll just use the AndroidManifest.xml - // to retrieve its signatures, not validating all of the - // files. - ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); - certs = loadCertificates(jarFile, jarEntry, readBuffer); - if (certs == null) { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates at entry " - + jarEntry.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } - if (DEBUG_JAR) { - Slog.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry - + " certs=" + (certs != null ? certs.length : 0)); - if (certs != null) { - final int N = certs.length; - for (int i=0; i<N; i++) { - Slog.i(TAG, " Public key: " - + certs[i][0].getPublicKey().getEncoded() - + " " + certs[i][0].getPublicKey()); - } - } - } - } else { - Iterator<ZipEntry> entries = jarFile.iterator(); - while (entries.hasNext()) { - final ZipEntry je = entries.next(); - if (je.isDirectory()) continue; + jarFile = new StrictJarFile(apkPath); - final String name = je.getName(); + // Always verify manifest, regardless of source + final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); + if (manifestEntry == null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Package " + apkPath + " has no manifest"); + } - if (name.startsWith("META-INF/")) - continue; + final List<ZipEntry> toVerify = new ArrayList<>(); + toVerify.add(manifestEntry); - if (ANDROID_MANIFEST_FILENAME.equals(name)) { - pkg.manifestDigest = - ManifestDigest.fromInputStream(jarFile.getInputStream(je)); - } + // If we're parsing an untrusted package, verify all contents + if ((flags & PARSE_IS_SYSTEM) == 0) { + final Iterator<ZipEntry> i = jarFile.iterator(); + while (i.hasNext()) { + final ZipEntry entry = i.next(); - final Certificate[][] localCerts = loadCertificates(jarFile, je, readBuffer); - if (DEBUG_JAR) { - Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName() - + ": certs=" + certs + " (" - + (certs != null ? certs.length : 0) + ")"); - } + if (entry.isDirectory()) continue; + if (entry.getName().startsWith("META-INF/")) continue; + if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue; - if (localCerts == null) { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates at entry " - + je.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } else if (certs == null) { - certs = localCerts; - } else { - // Ensure all certificates match. - for (int i=0; i<certs.length; i++) { - boolean found = false; - for (int j=0; j<localCerts.length; j++) { - if (certs[i] != null && - certs[i].equals(localCerts[j])) { - found = true; - break; - } - } - if (!found || certs.length != localCerts.length) { - Slog.e(TAG, "Package " + pkg.packageName - + " has mismatched certificates at entry " - + je.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; - return false; - } - } - } + toVerify.add(entry); } } - jarFile.close(); - - synchronized (mSync) { - mReadBuffer = readBufferRef; - } - if (!ArrayUtils.isEmpty(certs)) { - pkg.mSignatures = convertToSignatures(certs); - } else { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates; ignoring!"); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } + // Verify that entries are signed consistently with the first entry + // we encountered. Note that for splits, certificates may have + // already been populated during an earlier parse of a base APK. + for (ZipEntry entry : toVerify) { + final Certificate[][] entryCerts = loadCertificates(jarFile, entry); + if (ArrayUtils.isEmpty(entryCerts)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "Package " + apkPath + " has no certificates at entry " + + entry.getName()); + } - // Add the signing KeySet to the system - pkg.mSigningKeys = new HashSet<PublicKey>(); - for (int i=0; i < certs.length; i++) { - pkg.mSigningKeys.add(certs[i][0].getPublicKey()); + if (pkg.mCertificates == null) { + pkg.mCertificates = entryCerts; + pkg.mSignatures = convertToSignatures(entryCerts); + pkg.mSigningKeys = new ArraySet<>(); + for (int i = 0; i < entryCerts.length; i++) { + pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey()); + } + } else { + final boolean certsMatch = (pkg.mCertificates.length == entryCerts.length) + && ArrayUtils.containsAll(pkg.mCertificates, entryCerts) + && ArrayUtils.containsAll(entryCerts, pkg.mCertificates); + if (!certsMatch) { + throw new PackageParserException( + INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath + + " has mismatched certificates at entry " + + entry.getName()); + } + } } - - } catch (CertificateEncodingException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (IOException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (SecurityException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (RuntimeException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; - return false; + } catch (GeneralSecurityException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, + "Failed to collect certificates from " + apkPath, e); + } finally { + closeQuietly(jarFile); } - - return true; } /** * Only collect certificates on the manifest; does not validate signatures * across remainder of package. */ - private static Signature[] collectCertificates(String packageFilePath) { + private static Signature[] collectManifestCertificates(File apkFile) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); try { - final StrictJarFile jarFile = new StrictJarFile(packageFilePath); + final StrictJarFile jarFile = new StrictJarFile(apkPath); try { final ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); - if (jarEntry != null) { - final Certificate[][] certs = loadCertificates(jarFile, jarEntry, null); - return convertToSignatures(certs); + if (jarEntry == null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Package " + apkPath + " has no manifest"); } + + final Certificate[][] certs = loadCertificates(jarFile, jarEntry); + return convertToSignatures(certs); + } finally { jarFile.close(); } - } catch (GeneralSecurityException e) { - Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e); - } catch (IOException e) { - Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e); + } catch (GeneralSecurityException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, + "Failed to collect certificates from " + apkPath, e); } - return null; } private static Signature[] convertToSignatures(Certificate[][] certs) @@ -879,67 +887,55 @@ public class PackageParser { return res; } - /* - * Utility method that retrieves just the package name and install - * location from the apk location at the given file path. - * @param packageFilePath file location of the apk - * @param flags Special parse flags - * @return PackageLite object with package information or null on failure. + /** + * Utility method that retrieves lightweight details about a single APK + * file, including package name, split name, and install location. + * + * @param apkFile path to a single APK + * @param flags optional parse flags, such as {@link #PARSE_GET_SIGNATURES} */ - public static ApkLite parseApkLite(String packageFilePath, int flags) { + public static ApkLite parseApkLite(File apkFile, int flags) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + AssetManager assmgr = null; - final XmlResourceParser parser; - final Resources res; + XmlResourceParser parser = null; try { assmgr = new AssetManager(); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); - int cookie = assmgr.addAssetPath(packageFilePath); + int cookie = assmgr.addAssetPath(apkPath); if (cookie == 0) { - return null; + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Failed to parse " + apkPath); } final DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); - res = new Resources(assmgr, metrics, null); + + final Resources res = new Resources(assmgr, metrics, null); parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); - } catch (Exception e) { - if (assmgr != null) assmgr.close(); - Slog.w(TAG, "Unable to read AndroidManifest.xml of " - + packageFilePath, e); - return null; - } - // Only collect certificates on the manifest; does not validate - // signatures across remainder of package. - final Signature[] signatures; - if ((flags & PARSE_GET_SIGNATURES) != 0) { - signatures = collectCertificates(packageFilePath); - } else { - signatures = null; - } + // Only collect certificates on the manifest; does not validate + // signatures across remainder of package. + final Signature[] signatures; + if ((flags & PARSE_GET_SIGNATURES) != 0) { + signatures = collectManifestCertificates(apkFile); + } else { + signatures = null; + } - final AttributeSet attrs = parser; - final String errors[] = new String[1]; - ApkLite packageLite = null; - try { - packageLite = parseApkLite(res, parser, attrs, flags, signatures, errors); - } catch (PackageParserException e) { - Slog.w(TAG, packageFilePath, e); - } catch (IOException e) { - Slog.w(TAG, packageFilePath, e); - } catch (XmlPullParserException e) { - Slog.w(TAG, packageFilePath, e); + final AttributeSet attrs = parser; + return parseApkLite(res, parser, attrs, flags, signatures); + + } catch (XmlPullParserException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to parse " + apkPath, e); } finally { if (parser != null) parser.close(); if (assmgr != null) assmgr.close(); } - if (packageLite == null) { - Slog.e(TAG, "parsePackageLite error: " + errors[0]); - return null; - } - return packageLite; } private static String validateName(String name, boolean requiresSeparator) { @@ -995,12 +991,16 @@ public class PackageParser { } } - final String splitName = attrs.getAttributeValue(null, "split"); + String splitName = attrs.getAttributeValue(null, "split"); if (splitName != null) { - final String error = validateName(splitName, true); - if (error != null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, - "Invalid manifest split: " + error); + if (splitName.length() == 0) { + splitName = null; + } else { + final String error = validateName(splitName, true); + if (error != null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + "Invalid manifest split: " + error); + } } } @@ -1009,8 +1009,8 @@ public class PackageParser { } private static ApkLite parseApkLite(Resources res, XmlPullParser parser, - AttributeSet attrs, int flags, Signature[] signatures, String[] outError) - throws IOException, XmlPullParserException, PackageParserException { + AttributeSet attrs, int flags, Signature[] signatures) throws IOException, + XmlPullParserException, PackageParserException { final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags); int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; @@ -1043,7 +1043,7 @@ public class PackageParser { } if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) { - final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags, outError); + final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags); if (verifier != null) { verifiers.add(verifier); } @@ -1793,7 +1793,7 @@ public class PackageParser { } } - owner.mKeySetMapping = new HashMap<String, Set<PublicKey>>(); + owner.mKeySetMapping = new ArrayMap<String, ArraySet<PublicKey>>(); for (Map.Entry<PublicKey, Set<String>> e : definedKeySets.entrySet()) { PublicKey key = e.getKey(); Set<String> keySetNames = e.getValue(); @@ -1801,7 +1801,7 @@ public class PackageParser { if (owner.mKeySetMapping.containsKey(alias)) { owner.mKeySetMapping.get(alias).add(key); } else { - Set<PublicKey> keys = new HashSet<PublicKey>(); + ArraySet<PublicKey> keys = new ArraySet<PublicKey>(); keys.add(key); owner.mKeySetMapping.put(alias, keys); } @@ -2088,6 +2088,11 @@ public class PackageParser { false)) { ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_fullBackupOnly, + false)) { + ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY; + } } } @@ -3427,8 +3432,7 @@ public class PackageParser { } private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser, - AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException, - IOException { + AttributeSet attrs, int flags) { final TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestPackageVerifier); @@ -3671,7 +3675,10 @@ public class PackageParser { public String packageName; // TODO: work towards making these paths invariant + + /** Base APK */ public String codePath; + /** Split APKs, ordered by parsed splitName */ public String[] splitCodePaths; // For now we only support one application per package. @@ -3717,7 +3724,8 @@ public class PackageParser { public int mSharedUserLabel; // Signatures that were read from the package. - public Signature mSignatures[]; + public Signature[] mSignatures; + public Certificate[][] mCertificates; // For use by package manager service for quick lookup of // preferred up order. @@ -3779,8 +3787,8 @@ public class PackageParser { /** * Data used to feed the KeySetManager */ - public Set<PublicKey> mSigningKeys; - public Map<String, Set<PublicKey>> mKeySetMapping; + public ArraySet<PublicKey> mSigningKeys; + public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping; public Package(String packageName) { this.packageName = packageName; @@ -3788,6 +3796,15 @@ public class PackageParser { applicationInfo.uid = -1; } + public Collection<String> getAllCodePaths() { + ArrayList<String> paths = new ArrayList<>(); + paths.add(codePath); + if (!ArrayUtils.isEmpty(splitCodePaths)) { + Collections.addAll(paths, splitCodePaths); + } + return paths; + } + public void setPackageName(String newName) { packageName = newName; applicationInfo.packageName = newName; @@ -4390,6 +4407,33 @@ public class PackageParser { sCompatibilityModeEnabled = compatibilityModeEnabled; } + private static AtomicReference<byte[]> sBuffer = new AtomicReference<byte[]>(); + + public static long readFullyIgnoringContents(InputStream in) throws IOException { + byte[] buffer = sBuffer.getAndSet(null); + if (buffer == null) { + buffer = new byte[4096]; + } + + int n = 0; + int count = 0; + while ((n = in.read(buffer, 0, buffer.length)) != -1) { + count += n; + } + + sBuffer.set(buffer); + return count; + } + + public static void closeQuietly(StrictJarFile jarFile) { + if (jarFile != null) { + try { + jarFile.close(); + } catch (Exception ignored) { + } + } + } + public static class PackageParserException extends Exception { public final int error; @@ -4397,5 +4441,10 @@ public class PackageParser { super(detailMessage); this.error = error; } + + public PackageParserException(int error, String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + this.error = error; + } } } diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 535bbe2..8142670 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -699,15 +699,15 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { break; } - try { - if (enable) { - return mPhoneService.enableApnType(apnType); - } else { - return mPhoneService.disableApnType(apnType); - } - } catch (RemoteException e) { - if (retry == 0) getPhoneService(true); - } +// try { +// if (enable) { +// return mPhoneService.enableApnType(apnType); +// } else { +// return mPhoneService.disableApnType(apnType); +// } +// } catch (RemoteException e) { +// if (retry == 0) getPhoneService(true); +// } } loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\""); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 757f38e..96db772 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -719,6 +719,26 @@ public class UserManager { /** * If the target user is a managed profile of the calling user or the caller + * is itself a managed profile, then this returns a copy of the label with + * badging for accessibility services like talkback. E.g. passing in "Email" + * and it might return "Work Email" for Email in the work profile. + * + * @param label The label to change. + * @param user The target user. + * @return A label that combines the original label and a badge as + * determined by the system. + */ + public String getBadgedLabelForUser(String label, UserHandle user) { + UserInfo userInfo = getUserIfProfile(user.getIdentifier()); + if (userInfo != null && userInfo.isManagedProfile()) { + return Resources.getSystem().getString( + R.string.managed_profile_label_badge, label); + } + return label; + } + + /** + * If the target user is a managed profile of the calling user or the caller * is itself a managed profile, then this returns a drawable to use as a small * icon to include in a view to distinguish it from the original icon. * diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 0418049..23b1e2c 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -1194,7 +1194,14 @@ public abstract class PreferenceActivity extends ListActivity implements * @param args Optional arguments to supply to the fragment. */ public void switchToHeader(String fragmentName, Bundle args) { - setSelectedHeader(null); + Header selectedHeader = null; + for (int i = 0; i < mHeaders.size(); i++) { + if (fragmentName.equals(mHeaders.get(i).fragment)) { + selectedHeader = mHeaders.get(i); + break; + } + } + setSelectedHeader(selectedHeader); switchToHeaderInner(fragmentName, args); } diff --git a/core/java/android/transition/Explode.java b/core/java/android/transition/Explode.java index fae527c..feb8efd 100644 --- a/core/java/android/transition/Explode.java +++ b/core/java/android/transition/Explode.java @@ -15,20 +15,16 @@ */ package android.transition; +import com.android.internal.R; + import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.graphics.Path; import android.graphics.Rect; import android.util.FloatMath; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; - /** * This transition tracks changes to the visibility of target views in the * start and end scenes and moves views in or out from the edges of the @@ -44,8 +40,7 @@ public class Explode extends Visibility { private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); private static final String TAG = "Explode"; - - private static final String PROPNAME_SCREEN_BOUNDS = "android:out:screenBounds"; + private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds"; private int[] mTempLoc = new int[2]; @@ -56,8 +51,8 @@ public class Explode extends Visibility { private void captureValues(TransitionValues transitionValues) { View view = transitionValues.view; view.getLocationOnScreen(mTempLoc); - int left = mTempLoc[0] + Math.round(view.getTranslationX()); - int top = mTempLoc[1] + Math.round(view.getTranslationY()); + int left = mTempLoc[0]; + int top = mTempLoc[1]; int right = left + view.getWidth(); int bottom = top + view.getHeight(); transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom)); @@ -75,27 +70,6 @@ public class Explode extends Visibility { captureValues(transitionValues); } - private Animator createAnimation(final View view, float startX, float startY, float endX, - float endY, float terminalX, float terminalY, TimeInterpolator interpolator) { - view.setTranslationX(startX); - view.setTranslationY(startY); - if (startY == endY && startX == endX) { - return null; - } - Path path = new Path(); - path.moveTo(startX, startY); - path.lineTo(endX, endY); - ObjectAnimator pathAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, - View.TRANSLATION_Y, path); - pathAnimator.setInterpolator(interpolator); - OutAnimatorListener listener = new OutAnimatorListener(view, terminalX, terminalY, - endX, endY); - pathAnimator.addListener(listener); - pathAnimator.addPauseListener(listener); - - return pathAnimator; - } - @Override public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { @@ -103,29 +77,43 @@ public class Explode extends Visibility { return null; } Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS); + float endX = view.getTranslationX(); + float endY = view.getTranslationY(); calculateOut(sceneRoot, bounds, mTempLoc); + float startX = endX + mTempLoc[0]; + float startY = endY + mTempLoc[1]; - final float endX = view.getTranslationX(); - final float startX = endX + mTempLoc[0]; - final float endY = view.getTranslationY(); - final float startY = endY + mTempLoc[1]; - - return createAnimation(view, startX, startY, endX, endY, endX, endY, sDecelerate); + return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top, + startX, startY, endX, endY, sDecelerate); } @Override public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { + if (startValues == null) { + return null; + } Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS); + int viewPosX = bounds.left; + int viewPosY = bounds.top; + float startX = view.getTranslationX(); + float startY = view.getTranslationY(); + float endX = startX; + float endY = startY; + int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transitionPosition); + if (interruptedPosition != null) { + // We want to have the end position relative to the interrupted position, not + // the position it was supposed to start at. + endX += interruptedPosition[0] - bounds.left; + endY += interruptedPosition[1] - bounds.top; + bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]); + } calculateOut(sceneRoot, bounds, mTempLoc); + endX += mTempLoc[0]; + endY += mTempLoc[1]; - final float startX = view.getTranslationX(); - final float endX = startX + mTempLoc[0]; - final float startY = view.getTranslationY(); - final float endY = startY + mTempLoc[1]; - - return createAnimation(view, startX, startY, endX, endY, startX, startY, - sAccelerate); + return TranslationAnimationCreator.createAnimation(view, startValues, + viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate); } private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) { @@ -153,8 +141,8 @@ public class Explode extends Visibility { if (xVector == 0 && yVector == 0) { // Random direction when View is centered on focal View. - xVector = (float)(Math.random() * 2) - 1; - yVector = (float)(Math.random() * 2) - 1; + xVector = (float) (Math.random() * 2) - 1; + yVector = (float) (Math.random() * 2) - 1; } float vectorSize = calculateDistance(xVector, yVector); xVector /= vectorSize; @@ -176,53 +164,4 @@ public class Explode extends Visibility { private static float calculateDistance(float x, float y) { return FloatMath.sqrt((x * x) + (y * y)); } - - private static class OutAnimatorListener extends AnimatorListenerAdapter { - private final View mView; - private boolean mCanceled = false; - private float mPausedX; - private float mPausedY; - private final float mTerminalX; - private final float mTerminalY; - private final float mEndX; - private final float mEndY; - - public OutAnimatorListener(View view, float terminalX, float terminalY, - float endX, float endY) { - mView = view; - mTerminalX = terminalX; - mTerminalY = terminalY; - mEndX = endX; - mEndY = endY; - } - - @Override - public void onAnimationCancel(Animator animator) { - mView.setTranslationX(mTerminalX); - mView.setTranslationY(mTerminalY); - mCanceled = true; - } - - @Override - public void onAnimationEnd(Animator animator) { - if (!mCanceled) { - mView.setTranslationX(mTerminalX); - mView.setTranslationY(mTerminalY); - } - } - - @Override - public void onAnimationPause(Animator animator) { - mPausedX = mView.getTranslationX(); - mPausedY = mView.getTranslationY(); - mView.setTranslationY(mEndX); - mView.setTranslationY(mEndY); - } - - @Override - public void onAnimationResume(Animator animator) { - mView.setTranslationX(mPausedX); - mView.setTranslationY(mPausedY); - } - } } diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java index 8269258..0d2e487 100644 --- a/core/java/android/transition/Slide.java +++ b/core/java/android/transition/Slide.java @@ -16,14 +16,7 @@ package android.transition; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.graphics.Rect; -import android.util.Log; -import android.util.Property; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -41,71 +34,60 @@ import android.view.animation.DecelerateInterpolator; */ public class Slide extends Visibility { private static final String TAG = "Slide"; - private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); - + private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition"; private CalculateSlide mSlideCalculator = sCalculateBottom; private interface CalculateSlide { - /** Returns the translation value for view when it out of the scene */ - float getGone(ViewGroup sceneRoot, View view); - /** Returns the translation value for view when it is in the scene */ - float getHere(View view); + /** Returns the translation value for view when it goes out of the scene */ + float getGoneX(ViewGroup sceneRoot, View view); - /** Returns the property to animate translation */ - Property<View, Float> getProperty(); + /** Returns the translation value for view when it goes out of the scene */ + float getGoneY(ViewGroup sceneRoot, View view); } private static abstract class CalculateSlideHorizontal implements CalculateSlide { - @Override - public float getHere(View view) { - return view.getTranslationX(); - } @Override - public Property<View, Float> getProperty() { - return View.TRANSLATION_X; + public float getGoneY(ViewGroup sceneRoot, View view) { + return view.getTranslationY(); } } private static abstract class CalculateSlideVertical implements CalculateSlide { - @Override - public float getHere(View view) { - return view.getTranslationY(); - } @Override - public Property<View, Float> getProperty() { - return View.TRANSLATION_Y; + public float getGoneX(ViewGroup sceneRoot, View view) { + return view.getTranslationX(); } } private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { @Override - public float getGone(ViewGroup sceneRoot, View view) { + public float getGoneX(ViewGroup sceneRoot, View view) { return view.getTranslationX() - sceneRoot.getWidth(); } }; private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { @Override - public float getGone(ViewGroup sceneRoot, View view) { + public float getGoneY(ViewGroup sceneRoot, View view) { return view.getTranslationY() - sceneRoot.getHeight(); } }; private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { @Override - public float getGone(ViewGroup sceneRoot, View view) { + public float getGoneX(ViewGroup sceneRoot, View view) { return view.getTranslationX() + sceneRoot.getWidth(); } }; private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { @Override - public float getGone(ViewGroup sceneRoot, View view) { + public float getGoneY(ViewGroup sceneRoot, View view) { return view.getTranslationY() + sceneRoot.getHeight(); } }; @@ -125,8 +107,28 @@ public class Slide extends Visibility { setSlideEdge(slideEdge); } + private void captureValues(TransitionValues transitionValues) { + View view = transitionValues.view; + int[] position = new int[2]; + view.getLocationOnScreen(position); + transitionValues.values.put(PROPNAME_SCREEN_POSITION, position); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureEndValues(transitionValues); + captureValues(transitionValues); + } + /** * Change the edge that Views appear and disappear from. + * * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP}, * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}. @@ -153,77 +155,35 @@ public class Slide extends Visibility { setPropagation(propagation); } - private Animator createAnimation(final View view, Property<View, Float> property, - float start, float end, float terminalValue, TimeInterpolator interpolator) { - view.setTranslationY(start); - if (start == end) { - return null; - } - final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end); - - SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end); - anim.addListener(listener); - anim.addPauseListener(listener); - anim.setInterpolator(interpolator); - return anim; - } - @Override public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { if (endValues == null) { return null; } - float end = mSlideCalculator.getHere(view); - float start = mSlideCalculator.getGone(sceneRoot, view); - return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate); + int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); + float endX = view.getTranslationX(); + float endY = view.getTranslationY(); + float startX = mSlideCalculator.getGoneX(sceneRoot, view); + float startY = mSlideCalculator.getGoneY(sceneRoot, view); + return TranslationAnimationCreator + .createAnimation(view, endValues, position[0], position[1], + startX, startY, endX, endY, sDecelerate); } @Override public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { - float start = mSlideCalculator.getHere(view); - float end = mSlideCalculator.getGone(sceneRoot, view); - - return createAnimation(view, mSlideCalculator.getProperty(), start, end, start, - sAccelerate); - } - - private static class SlideAnimatorListener extends AnimatorListenerAdapter { - private boolean mCanceled = false; - private float mPausedY; - private final View mView; - private final float mEndY; - private final float mTerminalY; - - public SlideAnimatorListener(View view, float terminalY, float endY) { - mView = view; - mTerminalY = terminalY; - mEndY = endY; - } - - @Override - public void onAnimationCancel(Animator animator) { - mView.setTranslationY(mTerminalY); - mCanceled = true; - } - - @Override - public void onAnimationEnd(Animator animator) { - if (!mCanceled) { - mView.setTranslationY(mTerminalY); - } - } - - @Override - public void onAnimationPause(Animator animator) { - mPausedY = mView.getTranslationY(); - mView.setTranslationY(mEndY); - } - - @Override - public void onAnimationResume(Animator animator) { - mView.setTranslationY(mPausedY); + if (startValues == null) { + return null; } + int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); + float startX = view.getTranslationX(); + float startY = view.getTranslationY(); + float endX = mSlideCalculator.getGoneX(sceneRoot, view); + float endY = mSlideCalculator.getGoneY(sceneRoot, view); + return TranslationAnimationCreator + .createAnimation(view, startValues, position[0], position[1], + startX, startY, endX, endY, sAccelerate); } } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 508769d..e936232 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -673,7 +673,7 @@ public abstract class Transition implements Cloneable { startDelays.put(mAnimators.size(), delay); minStartDelay = Math.min(delay, minStartDelay); } - AnimationInfo info = new AnimationInfo(view, getName(), + AnimationInfo info = new AnimationInfo(view, getName(), this, sceneRoot.getWindowId(), infoValues); runningAnimators.put(animator, info); mAnimators.add(animator); @@ -1587,30 +1587,10 @@ public abstract class Transition implements Cloneable { AnimationInfo oldInfo = runningAnimators.get(anim); if (oldInfo != null && oldInfo.view != null && oldInfo.view.getContext() == sceneRoot.getContext()) { - boolean cancel = false; TransitionValues oldValues = oldInfo.values; View oldView = oldInfo.view; TransitionValues newValues = mEndValues.viewValues.get(oldView); - if (oldValues != null) { - // if oldValues null, then transition didn't care to stash values, - // and won't get canceled - if (newValues != null) { - for (String key : oldValues.values.keySet()) { - Object oldValue = oldValues.values.get(key); - Object newValue = newValues.values.get(key); - if (oldValue != null && newValue != null && - !oldValue.equals(newValue)) { - cancel = true; - if (DBG) { - Log.d(LOG_TAG, "Transition.playTransition: " + - "oldValue != newValue for " + key + - ": old, new = " + oldValue + ", " + newValue); - } - break; - } - } - } - } + boolean cancel = oldInfo.transition.areValuesChanged(oldValues, newValues); if (cancel) { if (anim.isRunning() || anim.isStarted()) { if (DBG) { @@ -1632,6 +1612,29 @@ public abstract class Transition implements Cloneable { runAnimators(); } + boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) { + boolean valuesChanged = false; + // if oldValues null, then transition didn't care to stash values, + // and won't get canceled + if (oldValues != null && newValues != null) { + for (String key : oldValues.values.keySet()) { + Object oldValue = oldValues.values.get(key); + Object newValue = newValues.values.get(key); + if (oldValue != null && newValue != null && + !oldValue.equals(newValue)) { + valuesChanged = true; + if (DBG) { + Log.d(LOG_TAG, "Transition.playTransition: " + + "oldValue != newValue for " + key + + ": old, new = " + oldValue + ", " + newValue); + } + break; + } + } + } + return valuesChanged; + } + /** * This is a utility method used by subclasses to handle standard parts of * setting up and running an Animator: it sets the {@link #getDuration() @@ -2070,12 +2073,15 @@ public abstract class Transition implements Cloneable { String name; TransitionValues values; WindowId windowId; + Transition transition; - AnimationInfo(View view, String name, WindowId windowId, TransitionValues values) { + AnimationInfo(View view, String name, Transition transition, + WindowId windowId, TransitionValues values) { this.view = view; this.name = name; this.values = values; this.windowId = windowId; + this.transition = transition; } } diff --git a/core/java/android/transition/TranslationAnimationCreator.java b/core/java/android/transition/TranslationAnimationCreator.java new file mode 100644 index 0000000..de71fd7 --- /dev/null +++ b/core/java/android/transition/TranslationAnimationCreator.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.transition; + +import com.android.internal.R; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.graphics.Path; +import android.view.View; + +/** + * This class is used by Slide and Explode to create an animator that goes from the start + * position to the end position. It takes into account the canceled position so that it + * will not blink out or shift suddenly when the transition is interrupted. + */ +class TranslationAnimationCreator { + + /** + * Creates an animator that can be used for x and/or y translations. When interrupted, + * it sets a tag to keep track of the position so that it may be continued from position. + * + * @param view The view being moved. This may be in the overlay for onDisappear. + * @param values The values containing the view in the view hierarchy. + * @param viewPosX The x screen coordinate of view + * @param viewPosY The y screen coordinate of view + * @param startX The start translation x of view + * @param startY The start translation y of view + * @param endX The end translation x of view + * @param endY The end translation y of view + * @param interpolator The interpolator to use with this animator. + * @return An animator that moves from (startX, startY) to (endX, endY) unless there was + * a previous interruption, in which case it moves from the current position to (endX, endY). + */ + static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY, + float startX, float startY, float endX, float endY, TimeInterpolator interpolator) { + float terminalX = view.getTranslationX(); + float terminalY = view.getTranslationY(); + int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition); + if (startPosition != null) { + startX = startPosition[0] - viewPosX + terminalX; + startY = startPosition[1] - viewPosY + terminalY; + } + // Initial position is at translation startX, startY, so position is offset by that amount + int startPosX = viewPosX + Math.round(startX - terminalX); + int startPosY = viewPosY + Math.round(startY - terminalY); + + view.setTranslationX(startX); + view.setTranslationY(startY); + if (startX == endX && startY == endY) { + return null; + } + Path path = new Path(); + path.moveTo(startX, startY); + path.lineTo(endX, endY); + ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y, + path); + + TransitionPositionListener listener = new TransitionPositionListener(view, values.view, + startPosX, startPosY, terminalX, terminalY); + anim.addListener(listener); + anim.addPauseListener(listener); + anim.setInterpolator(interpolator); + return anim; + } + + private static class TransitionPositionListener extends AnimatorListenerAdapter { + + private final View mViewInHierarchy; + private final View mMovingView; + private final int mStartX; + private final int mStartY; + private int[] mTransitionPosition; + private float mPausedX; + private float mPausedY; + private final float mTerminalX; + private final float mTerminalY; + + private TransitionPositionListener(View movingView, View viewInHierarchy, + int startX, int startY, float terminalX, float terminalY) { + mMovingView = movingView; + mViewInHierarchy = viewInHierarchy; + mStartX = startX - Math.round(mMovingView.getTranslationX()); + mStartY = startY - Math.round(mMovingView.getTranslationY()); + mTerminalX = terminalX; + mTerminalY = terminalY; + mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transitionPosition); + if (mTransitionPosition != null) { + mViewInHierarchy.setTagInternal(R.id.transitionPosition, null); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mTransitionPosition == null) { + mTransitionPosition = new int[2]; + } + mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX()); + mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY()); + mViewInHierarchy.setTagInternal(R.id.transitionPosition, mTransitionPosition); + } + + @Override + public void onAnimationEnd(Animator animator) { + mMovingView.setTranslationX(mTerminalX); + mMovingView.setTranslationY(mTerminalY); + } + + @Override + public void onAnimationPause(Animator animator) { + mPausedX = mMovingView.getTranslationX(); + mPausedY = mMovingView.getTranslationY(); + mMovingView.setTranslationX(mTerminalX); + mMovingView.setTranslationY(mTerminalY); + } + + @Override + public void onAnimationResume(Animator animator) { + mMovingView.setTranslationX(mPausedX); + mMovingView.setTranslationY(mPausedY); + } + } + +} diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index c6c8337..947e1a7 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -331,16 +331,6 @@ public abstract class Visibility extends Transition { public void onAnimationEnd(Animator animation) { finalSceneRoot.getOverlay().remove(finalOverlayView); } - - @Override - public void onAnimationPause(Animator animation) { - finalSceneRoot.getOverlay().remove(finalOverlayView); - } - - @Override - public void onAnimationResume(Animator animation) { - finalSceneRoot.getOverlay().add(finalOverlayView); - } }); } return animator; @@ -409,6 +399,16 @@ public abstract class Visibility extends Transition { return overlayView; } + @Override + boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) { + VisibilityInfo changeInfo = getVisibilityChangeInfo(oldValues, newValues); + if (oldValues == null && newValues == null) { + return false; + } + return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE || + changeInfo.endVisibility == View.VISIBLE); + } + /** * The default implementation of this method returns a null Animator. Subclasses should * override this method to make targets disappear with the desired transition. The diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 181f77e..76a6f52 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -606,7 +606,6 @@ public final class Display { * <p> * Apps generally do not need to be aware of this. It's only useful for fine-grained * A/V synchronization. - * @hide */ public long getAppVsyncOffsetNanos() { synchronized (this) { @@ -625,7 +624,6 @@ public final class Display { * {@link android.media.MediaCodec#releaseOutputBuffer(int, long)}. Times are * expressed in nanoseconds, using the system monotonic clock * ({@link System#nanoTime}). - * @hide */ public long getPresentationDeadlineNanos() { synchronized (this) { diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 446bbc9..68e2146 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -804,20 +804,12 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPicture(Picture picture) { - if (picture.createdFromStream) { - return; - } - picture.endRecording(); // TODO: Implement rendering } @Override public void drawPicture(Picture picture, Rect dst) { - if (picture.createdFromStream) { - return; - } - save(); translate(dst.left, dst.top); if (picture.getWidth() > 0 && picture.getHeight() > 0) { @@ -829,10 +821,6 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPicture(Picture picture, RectF dst) { - if (picture.createdFromStream) { - return; - } - save(); translate(dst.left, dst.top); if (picture.getWidth() > 0 && picture.getHeight() > 0) { @@ -981,22 +969,24 @@ class GLES20Canvas extends HardwareCanvas { } nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count, - long path, float hOffset, float vOffset, int bidiFlags, long nativePaint); + long path, float hOffset, float vOffset, int bidiFlags, long nativePaint, + long typeface); @Override public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) { if (text.length() == 0) return; nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } private static native void nDrawTextOnPath(long renderer, String text, int start, int end, - long path, float hOffset, float vOffset, int bidiFlags, long nativePaint); + long path, float hOffset, float vOffset, int bidiFlags, long nativePaint, + long typeface); @Override public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 9156216..89c2f37 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -45,7 +45,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.Shader; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManagerGlobal; @@ -4822,20 +4821,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final float x = r.exactCenterX(); final float y = r.exactCenterY(); - setDrawableHotspot(x, y); - } - - /** - * Sets the hotspot position for this View's drawables. - * - * @param x hotspot x coordinate - * @param y hotspot y coordinate - * @hide - */ - protected void setDrawableHotspot(float x, float y) { - if (mBackground != null) { - mBackground.setHotspot(x, y); - } + drawableHotspotChanged(x, y); } /** @@ -6798,7 +6784,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private void setPressed(boolean pressed, float x, float y) { if (pressed) { - setDrawableHotspot(x, y); + drawableHotspotChanged(x, y); } setPressed(pressed); @@ -9149,7 +9135,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; case MotionEvent.ACTION_MOVE: - setDrawableHotspot(x, y); + drawableHotspotChanged(x, y); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { @@ -15531,6 +15517,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * This function is called whenever the drawable hotspot changes. + * <p> + * Be sure to call through to the superclass when overriding this function. + * + * @param x hotspot x coordinate + * @param y hotspot y coordinate + */ + public void drawableHotspotChanged(float x, float y) { + if (mBackground != null) { + mBackground.setHotspot(x, y); + } + } + + /** * Call this to force a view to update its drawable state. This will cause * drawableStateChanged to be called on this view. Views that are interested * in the new state should call getDrawableState. diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index eb3f882..7e2d809 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -24,7 +24,6 @@ import android.graphics.Canvas; import android.graphics.Insets; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.PorterDuff.Mode; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -343,7 +342,10 @@ public abstract class AbsSeekBar extends ProgressBar { @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); - if (mThumb != null) mThumb.jumpToCurrentState(); + + if (mThumb != null) { + mThumb.jumpToCurrentState(); + } } @Override @@ -361,29 +363,12 @@ public abstract class AbsSeekBar extends ProgressBar { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); - final Drawable progressDrawable = getProgressDrawable(); - if (progressDrawable != null) { - progressDrawable.setHotspot(x, y); - } - - final Drawable thumb = mThumb; - if (thumb != null) { - thumb.setHotspot(x, y); - } - } - - @Override - public void invalidateDrawable(Drawable dr) { - super.invalidateDrawable(dr); - - if (dr == mThumb) { - // Handle changes to thumb width and height. - requestLayout(); + if (mThumb != null) { + mThumb.setHotspot(x, y); } } @@ -479,11 +464,11 @@ public abstract class AbsSeekBar extends ProgressBar { final Drawable background = getBackground(); if (background != null) { - final Rect bounds = mThumb.getBounds(); + final Rect bounds = thumb.getBounds(); final int offsetX = mPaddingLeft - mThumbOffset; final int offsetY = mPaddingTop; - background.setHotspotBounds(left + offsetX, bounds.top + offsetY, - right + offsetX, bounds.bottom + offsetY); + background.setHotspotBounds(left + offsetX, top + offsetY, + right + offsetX, bottom + offsetY); } // Canvas will be translated, so 0,0 is where we start drawing @@ -505,8 +490,8 @@ public abstract class AbsSeekBar extends ProgressBar { @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); - drawThumb(canvas); + } @Override diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index 7113793..4aa2300 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -307,10 +307,9 @@ public class CheckedTextView extends TextView implements Checkable { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mCheckMarkDrawable != null) { mCheckMarkDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 747d2b1..9ba0fe1 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; @@ -431,10 +430,9 @@ public abstract class CompoundButton extends Button implements Checkable { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mButtonDrawable != null) { mButtonDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 5a14929..34f333e 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -26,7 +26,6 @@ import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; @@ -223,10 +222,9 @@ public class FrameLayout extends ViewGroup { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mForeground != null) { mForeground.setHotspot(x, y); diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 399e087..5d578ca 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -32,7 +32,6 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Xfermode; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -1109,10 +1108,9 @@ public class ImageView extends View { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mDrawable != null) { mDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 62a8bec..394b255 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1623,10 +1623,9 @@ public class ProgressBar extends View { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mProgressDrawable != null) { mProgressDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java index 14d782d..23fa402 100644 --- a/core/java/android/widget/QuickContactBadge.java +++ b/core/java/android/widget/QuickContactBadge.java @@ -112,10 +112,9 @@ public class QuickContactBadge extends ImageView implements OnClickListener { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mOverlay != null) { mOverlay.setHotspot(x, y); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index f7d20b53..82637a1 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1781,7 +1781,9 @@ public class RemoteViews implements Parcelable, Filter { Parcel p = Parcel.obtain(); writeToParcel(p, 0); p.setDataPosition(0); - return new RemoteViews(p); + RemoteViews rv = new RemoteViews(p); + p.recycle(); + return rv; } public String getPackage() { diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 03193a2..cca29cf 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -962,10 +962,9 @@ public class Switch extends CompoundButton { invalidate(); } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mThumbDrawable != null) { mThumbDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 0f51e8b..d470586 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3503,10 +3503,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); final Drawables dr = mDrawables; if (dr != null) { diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 002573e..97b1634 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -45,10 +45,9 @@ public class SwipeDismissLayout extends FrameLayout { /** * Called when the layout has been swiped and the position of the window should change. * - * @param progress A number in [-1, 1] representing how far to the left - * or right the window has been swiped. Negative values are swipes - * left, and positives are right. - * @param translate A number in [-w, w], where w is the width of the + * @param progress A number in [0, 1] representing how far to the + * right the window has been swiped + * @param translate A number in [0, w], where w is the width of the * layout. This is equivalent to progress * layout.getWidth(). */ void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate); @@ -207,7 +206,7 @@ public class SwipeDismissLayout extends FrameLayout { private void setProgress(float deltaX) { mTranslationX = deltaX; - if (mProgressListener != null) { + if (mProgressListener != null && deltaX >= 0) { mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX); } } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 15dfed1..cb00062 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -89,7 +89,7 @@ LOCAL_SRC_FILES:= \ android_util_Process.cpp \ android_util_StringBlock.cpp \ android_util_XmlBlock.cpp \ - android/graphics/AndroidPicture.cpp \ + android_graphics_Picture.cpp \ android/graphics/AutoDecodeCancel.cpp \ android/graphics/Bitmap.cpp \ android/graphics/BitmapFactory.cpp \ diff --git a/core/jni/android/graphics/AndroidPicture.cpp b/core/jni/android/graphics/AndroidPicture.cpp deleted file mode 100644 index 5977ab2..0000000 --- a/core/jni/android/graphics/AndroidPicture.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "AndroidPicture.h" -#include "SkCanvas.h" -#include "SkStream.h" - -AndroidPicture::AndroidPicture(const AndroidPicture* src) { - if (NULL != src) { - mWidth = src->width(); - mHeight = src->height(); - if (NULL != src->mPicture.get()) { - mPicture.reset(SkRef(src->mPicture.get())); - } if (NULL != src->mRecorder.get()) { - mPicture.reset(src->makePartialCopy()); - } - } else { - mWidth = 0; - mHeight = 0; - } -} - -SkCanvas* AndroidPicture::beginRecording(int width, int height) { - mPicture.reset(NULL); - mRecorder.reset(new SkPictureRecorder); - mWidth = width; - mHeight = height; - return mRecorder->beginRecording(width, height, NULL, 0); -} - -void AndroidPicture::endRecording() { - if (NULL != mRecorder.get()) { - mPicture.reset(mRecorder->endRecording()); - mRecorder.reset(NULL); - } -} - -int AndroidPicture::width() const { - if (NULL != mPicture.get()) { - SkASSERT(mPicture->width() == mWidth); - SkASSERT(mPicture->height() == mHeight); - } - - return mWidth; -} - -int AndroidPicture::height() const { - if (NULL != mPicture.get()) { - SkASSERT(mPicture->width() == mWidth); - SkASSERT(mPicture->height() == mHeight); - } - - return mHeight; -} - -AndroidPicture* AndroidPicture::CreateFromStream(SkStream* stream) { - AndroidPicture* newPict = new AndroidPicture; - - newPict->mPicture.reset(SkPicture::CreateFromStream(stream)); - if (NULL != newPict->mPicture.get()) { - newPict->mWidth = newPict->mPicture->width(); - newPict->mHeight = newPict->mPicture->height(); - } - - return newPict; -} - -void AndroidPicture::serialize(SkWStream* stream) const { - if (NULL != mRecorder.get()) { - SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy()); - tempPict->serialize(stream); - } else if (NULL != mPicture.get()) { - mPicture->serialize(stream); - } else { - SkPicture empty; - empty.serialize(stream); - } -} - -void AndroidPicture::draw(SkCanvas* canvas) { - if (NULL != mRecorder.get()) { - this->endRecording(); - SkASSERT(NULL != mPicture.get()); - } - if (NULL != mPicture.get()) { - // TODO: remove this const_cast once pictures are immutable - const_cast<SkPicture*>(mPicture.get())->draw(canvas); - } -} - -SkPicture* AndroidPicture::makePartialCopy() const { - SkASSERT(NULL != mRecorder.get()); - - SkPictureRecorder reRecorder; - - SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0); - mRecorder->partialReplay(canvas); - return reRecorder.endRecording(); -} diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 4584c46..69e149e 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -1123,29 +1123,82 @@ public: delete[] posPtr; } +#ifdef USE_MINIKIN + class DrawTextOnPathFunctor { + public: + DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset, + float vOffset, SkPaint* paint, SkPath* path) + : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset), + paint(paint), path(path) { + } + void operator()(size_t start, size_t end) { + uint16_t glyphs[1]; + for (size_t i = start; i < end; i++) { + glyphs[0] = layout.getGlyphId(i); + float x = hOffset + layout.getX(i); + float y = vOffset + layout.getY(i); + canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint); + } + } + private: + const Layout& layout; + SkCanvas* canvas; + float hOffset; + float vOffset; + SkPaint* paint; + SkPath* path; + }; +#endif + + static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags, + float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) { +#ifdef USE_MINIKIN + Layout layout; + std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface); + layout.doLayout(text, 0, count, count, css); + hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path); + // Set align to left for drawing, as we don't want individual + // glyphs centered or right-aligned; the offset above takes + // care of all alignment. + SkPaint::Align align = paint->getTextAlign(); + paint->setTextAlign(SkPaint::kLeft_Align); + + DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path); + MinikinUtils::forFontRun(layout, paint, f); + paint->setTextAlign(align); +#else + TextLayout::drawTextOnPath(paint, text, count, bidiFlags, hOffset, vOffset, path, canvas); +#endif + } + static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index, jint count, - jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) { + jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle, + jlong typefaceHandle) { SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPath* path = reinterpret_cast<SkPath*>(pathHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); jchar* textArray = env->GetCharArrayElements(text, NULL); - TextLayout::drawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset, - path, canvas); + doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset, + path, canvas, typeface); env->ReleaseCharArrayElements(text, textArray, 0); } static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject, jlong canvasHandle, jstring text, jlong pathHandle, - jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) { + jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle, + jlong typefaceHandle) { SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPath* path = reinterpret_cast<SkPath*>(pathHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); + const jchar* text_ = env->GetStringChars(text, NULL); int count = env->GetStringLength(text); - TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset, - path, canvas); + doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset, + path, canvas, typeface); env->ReleaseStringChars(text, text_); } @@ -1271,9 +1324,9 @@ static JNINativeMethod gCanvasMethods[] = { (void*) SkCanvasGlue::drawPosText___CII_FPaint}, {"native_drawPosText","(JLjava/lang/String;[FJ)V", (void*) SkCanvasGlue::drawPosText__String_FPaint}, - {"native_drawTextOnPath","(J[CIIJFFIJ)V", + {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint}, - {"native_drawTextOnPath","(JLjava/lang/String;JFFIJ)V", + {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint}, {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches}, diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index a4337cc..2bd7a28 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -3,7 +3,6 @@ #include "jni.h" #include "JNIHelp.h" #include "GraphicsJNI.h" -#include "AndroidPicture.h" #include "SkCanvas.h" #include "SkDevice.h" @@ -346,17 +345,6 @@ android::TypefaceImpl* GraphicsJNI::getNativeTypeface(JNIEnv* env, jobject paint return p; } -AndroidPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) -{ - SkASSERT(env); - SkASSERT(picture); - SkASSERT(env->IsInstanceOf(picture, gPicture_class)); - jlong pictureHandle = env->GetLongField(picture, gPicture_nativeInstanceID); - AndroidPicture* p = reinterpret_cast<AndroidPicture*>(pictureHandle); - SkASSERT(p); - return p; -} - SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) { SkASSERT(env); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 2e2f920..ad174f7 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -14,7 +14,6 @@ class SkBitmapRegionDecoder; class SkCanvas; class SkPaint; -class AndroidPicture; class GraphicsJNI { public: @@ -50,7 +49,6 @@ public: static SkPaint* getNativePaint(JNIEnv*, jobject paint); static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint); static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap); - static AndroidPicture* getNativePicture(JNIEnv*, jobject picture); static SkRegion* getNativeRegion(JNIEnv*, jobject region); // Given the 'native' long held by the Rasterizer.java object, return a diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp index a9360ea..fc92d53 100644 --- a/core/jni/android/graphics/MinikinUtils.cpp +++ b/core/jni/android/graphics/MinikinUtils.cpp @@ -19,6 +19,7 @@ #include <string> #include "SkPaint.h" +#include "SkPathMeasure.h" #include "minikin/Layout.h" #include "TypefaceImpl.h" #include "MinikinSkia.h" @@ -76,4 +77,20 @@ float MinikinUtils::xOffsetForTextAlign(SkPaint* paint, const Layout& layout) { return 0; } +float MinikinUtils::hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path) { + float align = 0; + switch (paint->getTextAlign()) { + case SkPaint::kCenter_Align: + align = -0.5f; + break; + case SkPaint::kRight_Align: + align = -1; + break; + default: + return 0; + } + SkPathMeasure measure(path, false); + return align * (layout.getAdvance() - measure.getLength()); +} + } diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h index a96c6b1..b2662a1 100644 --- a/core/jni/android/graphics/MinikinUtils.h +++ b/core/jni/android/graphics/MinikinUtils.h @@ -36,6 +36,7 @@ public: static float xOffsetForTextAlign(SkPaint* paint, const Layout& layout); + static float hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path); // f is a functor of type void f(size_t start, size_t end); template <typename F> static void forFontRun(const Layout& layout, SkPaint* paint, F& f) { diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp index 0683f73..bc0c25f 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/core/jni/android/graphics/Picture.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,123 +14,104 @@ * limitations under the License. */ -#include "jni.h" -#include "GraphicsJNI.h" -#include <android_runtime/AndroidRuntime.h> -#include "AndroidPicture.h" +#include "Picture.h" #include "SkCanvas.h" #include "SkStream.h" -#include "SkTemplates.h" -#include "CreateJavaOutputStreamAdaptor.h" namespace android { -class SkPictureGlue { -public: - static jlong newPicture(JNIEnv* env, jobject, jlong srcHandle) { - const AndroidPicture* src = reinterpret_cast<AndroidPicture*>(srcHandle); - return reinterpret_cast<jlong>(new AndroidPicture(src)); - } - - static jlong deserialize(JNIEnv* env, jobject, jobject jstream, - jbyteArray jstorage) { - AndroidPicture* picture = NULL; - SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); - if (strm) { - picture = AndroidPicture::CreateFromStream(strm); - delete strm; +Picture::Picture(const Picture* src) { + if (NULL != src) { + mWidth = src->width(); + mHeight = src->height(); + if (NULL != src->mPicture.get()) { + mPicture.reset(SkRef(src->mPicture.get())); + } if (NULL != src->mRecorder.get()) { + mPicture.reset(src->makePartialCopy()); } - return reinterpret_cast<jlong>(picture); + } else { + mWidth = 0; + mHeight = 0; } +} - static void killPicture(JNIEnv* env, jobject, jlong pictureHandle) { - AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); - SkASSERT(picture); - delete picture; - } +SkCanvas* Picture::beginRecording(int width, int height) { + mPicture.reset(NULL); + mRecorder.reset(new SkPictureRecorder); + mWidth = width; + mHeight = height; + return mRecorder->beginRecording(width, height, NULL, 0); +} - static void draw(JNIEnv* env, jobject, jlong canvasHandle, - jlong pictureHandle) { - SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); - AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); - SkASSERT(canvas); - SkASSERT(picture); - picture->draw(canvas); +void Picture::endRecording() { + if (NULL != mRecorder.get()) { + mPicture.reset(mRecorder->endRecording()); + mRecorder.reset(NULL); } +} - static jboolean serialize(JNIEnv* env, jobject, jlong pictureHandle, - jobject jstream, jbyteArray jstorage) { - AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); - SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); - - if (NULL != strm) { - picture->serialize(strm); - delete strm; - return JNI_TRUE; - } - return JNI_FALSE; +int Picture::width() const { + if (NULL != mPicture.get()) { + SkASSERT(mPicture->width() == mWidth); + SkASSERT(mPicture->height() == mHeight); } - static jint getWidth(JNIEnv* env, jobject jpic) { - NPE_CHECK_RETURN_ZERO(env, jpic); - AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic); - int width = pict->width(); - return static_cast<jint>(width); - } + return mWidth; +} - static jint getHeight(JNIEnv* env, jobject jpic) { - NPE_CHECK_RETURN_ZERO(env, jpic); - AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic); - int height = pict->height(); - return static_cast<jint>(height); +int Picture::height() const { + if (NULL != mPicture.get()) { + SkASSERT(mPicture->width() == mWidth); + SkASSERT(mPicture->height() == mHeight); } - static jlong beginRecording(JNIEnv* env, jobject, jlong pictHandle, - jint w, jint h) { - AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle); - // beginRecording does not ref its return value, it just returns it. - SkCanvas* canvas = pict->beginRecording(w, h); - // the java side will wrap this guy in a Canvas.java, which will call - // unref in its finalizer, so we have to ref it here, so that both that - // Canvas.java and our picture can both be owners - canvas->ref(); - return reinterpret_cast<jlong>(canvas); - } + return mHeight; +} - static void endRecording(JNIEnv* env, jobject, jlong pictHandle) { - AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle); - pict->endRecording(); - } -}; +Picture* Picture::CreateFromStream(SkStream* stream) { + Picture* newPict = new Picture; -static JNINativeMethod gPictureMethods[] = { - {"getWidth", "()I", (void*) SkPictureGlue::getWidth}, - {"getHeight", "()I", (void*) SkPictureGlue::getHeight}, - {"nativeConstructor", "(J)J", (void*) SkPictureGlue::newPicture}, - {"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)SkPictureGlue::deserialize}, - {"nativeBeginRecording", "(JII)J", (void*) SkPictureGlue::beginRecording}, - {"nativeEndRecording", "(J)V", (void*) SkPictureGlue::endRecording}, - {"nativeDraw", "(JJ)V", (void*) SkPictureGlue::draw}, - {"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)SkPictureGlue::serialize}, - {"nativeDestructor","(J)V", (void*) SkPictureGlue::killPicture} -}; + newPict->mPicture.reset(SkPicture::CreateFromStream(stream)); + if (NULL != newPict->mPicture.get()) { + newPict->mWidth = newPict->mPicture->width(); + newPict->mHeight = newPict->mPicture->height(); + } -#include <android_runtime/AndroidRuntime.h> + return newPict; +} -#define REG(env, name, array) \ - result = android::AndroidRuntime::registerNativeMethods(env, name, array, \ - SK_ARRAY_COUNT(array)); \ - if (result < 0) return result +void Picture::serialize(SkWStream* stream) const { + if (NULL != mRecorder.get()) { + SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy()); + tempPict->serialize(stream); + } else if (NULL != mPicture.get()) { + mPicture->serialize(stream); + } else { + SkPicture empty; + empty.serialize(stream); + } +} -int register_android_graphics_Picture(JNIEnv* env) { - int result; +void Picture::draw(SkCanvas* canvas) { + if (NULL != mRecorder.get()) { + this->endRecording(); + SkASSERT(NULL != mPicture.get()); + } + if (NULL != mPicture.get()) { + // TODO: remove this const_cast once pictures are immutable + const_cast<SkPicture*>(mPicture.get())->draw(canvas); + } +} - REG(env, "android/graphics/Picture", gPictureMethods); +SkPicture* Picture::makePartialCopy() const { + SkASSERT(NULL != mRecorder.get()); - return result; -} + SkPictureRecorder reRecorder; + SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0); + mRecorder->partialReplay(canvas); + return reRecorder.endRecording(); } - +}; // namespace android diff --git a/core/jni/android/graphics/AndroidPicture.h b/core/jni/android/graphics/Picture.h index f434941..abb0403 100644 --- a/core/jni/android/graphics/AndroidPicture.h +++ b/core/jni/android/graphics/Picture.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_PICTURE_H -#define ANDROID_PICTURE_H +#ifndef ANDROID_GRAPHICS_PICTURE_H +#define ANDROID_GRAPHICS_PICTURE_H #include "SkPicture.h" #include "SkPictureRecorder.h" @@ -28,13 +28,15 @@ class SkPictureRecorder; class SkStream; class SkWStream; +namespace android { + // Skia's SkPicture class has been split into an SkPictureRecorder // and an SkPicture. AndroidPicture recreates the functionality // of the old SkPicture interface by flip-flopping between the two // new classes. -class AndroidPicture { +class Picture { public: - explicit AndroidPicture(const AndroidPicture* src = NULL); + explicit Picture(const Picture* src = NULL); SkCanvas* beginRecording(int width, int height); @@ -44,7 +46,7 @@ public: int height() const; - static AndroidPicture* CreateFromStream(SkStream* stream); + static Picture* CreateFromStream(SkStream* stream); void serialize(SkWStream* stream) const; @@ -60,4 +62,6 @@ private: // resulting picture will have balanced saves and restores. SkPicture* makePartialCopy() const; }; -#endif // ANDROID_PICTURE_H + +}; // namespace android +#endif // ANDROID_GRAPHICS_PICTURE_H diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp new file mode 100644 index 0000000..f827907 --- /dev/null +++ b/core/jni/android_graphics_Picture.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" +#include "GraphicsJNI.h" +#include <android_runtime/AndroidRuntime.h> + +#include "Picture.h" + +#include "SkCanvas.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "CreateJavaOutputStreamAdaptor.h" + +namespace android { + +static jlong android_graphics_Picture_newPicture(JNIEnv* env, jobject, jlong srcHandle) { + const Picture* src = reinterpret_cast<Picture*>(srcHandle); + return reinterpret_cast<jlong>(new Picture(src)); +} + +static jlong android_graphics_Picture_deserialize(JNIEnv* env, jobject, jobject jstream, + jbyteArray jstorage) { + Picture* picture = NULL; + SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); + if (strm) { + picture = Picture::CreateFromStream(strm); + delete strm; + } + return reinterpret_cast<jlong>(picture); +} + +static void android_graphics_Picture_killPicture(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* picture = reinterpret_cast<Picture*>(pictureHandle); + SkASSERT(picture); + delete picture; +} + +static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle, + jlong pictureHandle) { + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); + Picture* picture = reinterpret_cast<Picture*>(pictureHandle); + SkASSERT(canvas); + SkASSERT(picture); + picture->draw(canvas); +} + +static jboolean android_graphics_Picture_serialize(JNIEnv* env, jobject, jlong pictureHandle, + jobject jstream, jbyteArray jstorage) { + Picture* picture = reinterpret_cast<Picture*>(pictureHandle); + SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + + if (NULL != strm) { + picture->serialize(strm); + delete strm; + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jint android_graphics_Picture_getWidth(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* pict = reinterpret_cast<Picture*>(pictureHandle); + return static_cast<jint>(pict->width()); +} + +static jint android_graphics_Picture_getHeight(JNIEnv* env, jobject, jlong pictureHandle) { + Picture* pict = reinterpret_cast<Picture*>(pictureHandle); + return static_cast<jint>(pict->height()); +} + +static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle, + jint w, jint h) { + Picture* pict = reinterpret_cast<Picture*>(pictHandle); + // beginRecording does not ref its return value, it just returns it. + SkCanvas* canvas = pict->beginRecording(w, h); + // the java side will wrap this guy in a Canvas.java, which will call + // unref in its finalizer, so we have to ref it here, so that both that + // Canvas.java and our picture can both be owners + canvas->ref(); + return reinterpret_cast<jlong>(canvas); +} + +static void android_graphics_Picture_endRecording(JNIEnv* env, jobject, jlong pictHandle) { + Picture* pict = reinterpret_cast<Picture*>(pictHandle); + pict->endRecording(); +} + +static JNINativeMethod gMethods[] = { + {"nativeGetWidth", "(J)I", (void*) android_graphics_Picture_getWidth}, + {"nativeGetHeight", "(J)I", (void*) android_graphics_Picture_getHeight}, + {"nativeConstructor", "(J)J", (void*) android_graphics_Picture_newPicture}, + {"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)android_graphics_Picture_deserialize}, + {"nativeBeginRecording", "(JII)J", (void*) android_graphics_Picture_beginRecording}, + {"nativeEndRecording", "(J)V", (void*) android_graphics_Picture_endRecording}, + {"nativeDraw", "(JJ)V", (void*) android_graphics_Picture_draw}, + {"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)android_graphics_Picture_serialize}, + {"nativeDestructor","(J)V", (void*) android_graphics_Picture_killPicture} +}; + +int register_android_graphics_Picture(JNIEnv* env) { + return AndroidRuntime::registerNativeMethods(env, "android/graphics/Picture", gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index a2b1ed9..807dd32 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -20,11 +20,15 @@ #include <system/audio.h> // keep these values in sync with AudioFormat.java -#define ENCODING_PCM_16BIT 2 -#define ENCODING_PCM_8BIT 3 -#define ENCODING_PCM_FLOAT 4 -#define ENCODING_INVALID 0 -#define ENCODING_DEFAULT 1 +#define ENCODING_PCM_16BIT 2 +#define ENCODING_PCM_8BIT 3 +#define ENCODING_PCM_FLOAT 4 +#define ENCODING_AC3 5 +#define ENCODING_E_AC3 6 +#define ENCODING_INVALID 0 +#define ENCODING_DEFAULT 1 + + #define CHANNEL_INVALID 0 #define CHANNEL_OUT_DEFAULT 1 @@ -38,6 +42,10 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_PCM_8_BIT; case ENCODING_PCM_FLOAT: return AUDIO_FORMAT_PCM_FLOAT; + case ENCODING_AC3: + return AUDIO_FORMAT_AC3; + case ENCODING_E_AC3: + return AUDIO_FORMAT_E_AC3; case ENCODING_DEFAULT: return AUDIO_FORMAT_DEFAULT; default: @@ -54,6 +62,10 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) return ENCODING_PCM_8BIT; case AUDIO_FORMAT_PCM_FLOAT: return ENCODING_PCM_FLOAT; + case AUDIO_FORMAT_AC3: + return ENCODING_AC3; + case AUDIO_FORMAT_E_AC3: + return ENCODING_E_AC3; case AUDIO_FORMAT_DEFAULT: return ENCODING_DEFAULT; default: diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index e548e91..264a9ae 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -220,8 +220,13 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, } // compute the frame count - const size_t bytesPerSample = audio_bytes_per_sample(format); - size_t frameCount = buffSizeInBytes / (channelCount * bytesPerSample); + size_t frameCount; + if (audio_is_linear_pcm(format)) { + const size_t bytesPerSample = audio_bytes_per_sample(format); + frameCount = buffSizeInBytes / (channelCount * bytesPerSample); + } else { + frameCount = buffSizeInBytes; + } jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { @@ -266,7 +271,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, format,// word length, PCM nativeChannelMask, frameCount, - AUDIO_OUTPUT_FLAG_NONE, + audio_is_linear_pcm(format) ? AUDIO_OUTPUT_FLAG_NONE : AUDIO_OUTPUT_FLAG_DIRECT, audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 0,// shared mem @@ -478,14 +483,6 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* da switch (format) { default: - // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT, - // AUDIO_FORMAT_PCM_8_BIT, and AUDIO_FORMAT_PCM_FLOAT, - // due to the limited set of values for audioFormat. - // The next section of the switch will probably work for more formats, but it has only - // been tested for AUDIO_FORMAT_PCM_16_BIT and AUDIO_FORMAT_PCM_FLOAT, - // so that's why the "default" case fails. - break; - case AUDIO_FORMAT_PCM_FLOAT: case AUDIO_FORMAT_PCM_16_BIT: { // writing to shared memory, check for capacity @@ -904,8 +901,12 @@ static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thi return -1; } const audio_format_t format = audioFormatToNative(audioFormat); - const size_t bytesPerSample = audio_bytes_per_sample(format); - return frameCount * channelCount * bytesPerSample; + if (audio_is_linear_pcm(format)) { + const size_t bytesPerSample = audio_bytes_per_sample(format); + return frameCount * channelCount * bytesPerSample; + } else { + return frameCount; + } } // ---------------------------------------------------------------------------- diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 4a6e117..de00e59 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -669,8 +669,48 @@ static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, #endif } +#ifdef USE_MINIKIN +class RenderTextOnPathFunctor { +public: + RenderTextOnPathFunctor(const Layout& layout, OpenGLRenderer* renderer, float hOffset, + float vOffset, SkPaint* paint, SkPath* path) + : layout(layout), renderer(renderer), hOffset(hOffset), vOffset(vOffset), + paint(paint), path(path) { + } + void operator()(size_t start, size_t end) { + uint16_t glyphs[1]; + for (size_t i = start; i < end; i++) { + glyphs[0] = layout.getGlyphId(i); + float x = hOffset + layout.getX(i); + float y = vOffset + layout.getY(i); + renderer->drawTextOnPath((const char*) glyphs, sizeof(glyphs), 1, path, x, y, paint); + } + } +private: + const Layout& layout; + OpenGLRenderer* renderer; + float hOffset; + float vOffset; + SkPaint* paint; + SkPath* path; +}; +#endif + static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count, - SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint) { + SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint, + TypefaceImpl* typeface) { +#ifdef USE_MINIKIN + Layout layout; + std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface); + layout.doLayout(text, 0, count, count, css); + hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path); + SkPaint::Align align = paint->getTextAlign(); + paint->setTextAlign(SkPaint::kLeft_Align); + + RenderTextOnPathFunctor f(layout, renderer, hOffset, vOffset, paint, path); + MinikinUtils::forFontRun(layout, paint, f); + paint->setTextAlign(align); +#else sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, text, 0, count, count, bidiFlags); if (value == NULL) { @@ -681,6 +721,7 @@ static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int co int bytesCount = glyphsCount * sizeof(jchar); renderer->drawTextOnPath((const char*) glyphs, bytesCount, glyphsCount, path, hOffset, vOffset, paint); +#endif } static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, @@ -739,27 +780,31 @@ static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject clazz, static void android_view_GLES20Canvas_drawTextArrayOnPath(JNIEnv* env, jobject clazz, jlong rendererPtr, jcharArray text, jint index, jint count, - jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) { + jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr, + jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); jchar* textArray = env->GetCharArrayElements(text, NULL); SkPath* path = reinterpret_cast<SkPath*>(pathPtr); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); renderTextOnPath(renderer, textArray + index, count, path, - hOffset, vOffset, bidiFlags, paint); + hOffset, vOffset, bidiFlags, paint, typeface); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz, jlong rendererPtr, jstring text, jint start, jint end, - jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) { + jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr, + jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); const jchar* textArray = env->GetStringChars(text, NULL); SkPath* path = reinterpret_cast<SkPath*>(pathPtr); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); renderTextOnPath(renderer, textArray + start, end - start, path, - hOffset, vOffset, bidiFlags, paint); + hOffset, vOffset, bidiFlags, paint, typeface); env->ReleaseStringChars(text, textArray); } @@ -986,8 +1031,8 @@ static JNINativeMethod gMethods[] = { { "nDrawText", "(JLjava/lang/String;IIFFIJJ)V", (void*) android_view_GLES20Canvas_drawText }, - { "nDrawTextOnPath", "(J[CIIJFFIJ)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath }, - { "nDrawTextOnPath", "(JLjava/lang/String;IIJFFIJ)V", + { "nDrawTextOnPath", "(J[CIIJFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath }, + { "nDrawTextOnPath", "(JLjava/lang/String;IIJFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextOnPath }, { "nDrawTextRun", "(J[CIIIIFFZJJ)V", (void*) android_view_GLES20Canvas_drawTextRunArray }, diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 815c4a7..2b94b65 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -49,6 +49,14 @@ using namespace android::uirenderer::renderthread; static jmethodID gRunnableMethod; +static JNIEnv* getenv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + class JavaTask : public RenderTask { public: JavaTask(JNIEnv* env, jobject jrunnable) { @@ -57,20 +65,13 @@ public: } virtual void run() { - env()->CallVoidMethod(mRunnable, gRunnableMethod); - env()->DeleteGlobalRef(mRunnable); + JNIEnv* env = getenv(mVm); + env->CallVoidMethod(mRunnable, gRunnableMethod); + env->DeleteGlobalRef(mRunnable); delete this; }; private: - JNIEnv* env() { - JNIEnv* env; - if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - return 0; - } - return env; - } - JavaVM* mVm; jobject mRunnable; }; @@ -122,12 +123,34 @@ private: std::vector<OnFinishedEvent> mOnFinishedEvents; }; -class RootRenderNode : public RenderNode, public AnimationHook { +class RenderingException : public MessageHandler { public: - RootRenderNode() : RenderNode() { + RenderingException(JavaVM* vm, const std::string& message) + : mVm(vm) + , mMessage(message) { + } + + virtual void handleMessage(const Message&) { + throwException(mVm, mMessage); + } + + static void throwException(JavaVM* vm, const std::string& message) { + JNIEnv* env = getenv(vm); + jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); + } + +private: + JavaVM* mVm; + std::string mMessage; +}; + +class RootRenderNode : public RenderNode, AnimationHook, ErrorHandler { +public: + RootRenderNode(JNIEnv* env) : RenderNode() { mLooper = Looper::getForThread(); LOG_ALWAYS_FATAL_IF(!mLooper.get(), "Must create RootRenderNode on a thread with a looper!"); + env->GetJavaVM(&mVm); } virtual ~RootRenderNode() {} @@ -137,10 +160,16 @@ public: mOnFinishedEvents.push_back(event); } + virtual void onError(const std::string& message) { + mLooper->sendMessage(new RenderingException(mVm, message), 0); + } + virtual void prepareTree(TreeInfo& info) { info.animationHook = this; + info.errorHandler = this; RenderNode::prepareTree(info); info.animationHook = NULL; + info.errorHandler = NULL; // post all the finished stuff if (mOnFinishedEvents.size()) { @@ -160,6 +189,7 @@ protected: private: sp<Looper> mLooper; std::vector<OnFinishedEvent> mOnFinishedEvents; + JavaVM* mVm; }; static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz, @@ -178,7 +208,7 @@ static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz, } static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) { - RootRenderNode* node = new RootRenderNode(); + RootRenderNode* node = new RootRenderNode(env); node->incStrong(0); node->setName("RootRenderNode"); return reinterpret_cast<jlong>(node); diff --git a/core/res/res/drawable-hdpi/ic_corp_badge.png b/core/res/res/drawable-hdpi/ic_corp_badge.png Binary files differdeleted file mode 100644 index c79ce92..0000000 --- a/core/res/res/drawable-hdpi/ic_corp_badge.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png Binary files differdeleted file mode 100644 index 0059e09..0000000 --- a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_corp_badge.png b/core/res/res/drawable-mdpi/ic_corp_badge.png Binary files differdeleted file mode 100644 index c1447fe..0000000 --- a/core/res/res/drawable-mdpi/ic_corp_badge.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png Binary files differdeleted file mode 100644 index 5ff8c5d..0000000 --- a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_corp_badge.png b/core/res/res/drawable-xhdpi/ic_corp_badge.png Binary files differdeleted file mode 100644 index 2d3d748..0000000 --- a/core/res/res/drawable-xhdpi/ic_corp_badge.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png Binary files differdeleted file mode 100644 index dc5716d..0000000 --- a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ic_corp_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_badge.png Binary files differdeleted file mode 100644 index 430e63b..0000000 --- a/core/res/res/drawable-xxhdpi/ic_corp_badge.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png Binary files differdeleted file mode 100644 index cc00dd8..0000000 --- a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png +++ /dev/null diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml new file mode 100644 index 0000000..16c101b --- /dev/null +++ b/core/res/res/drawable/ic_corp_badge.xml @@ -0,0 +1,43 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="20.0dp" + android:height="20.0dp"/> + + <viewport + android:viewportWidth="20.0" + android:viewportHeight="20.0"/> + + <path + android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0" + android:fill="#FF5722"/> + <path + android:pathData="M11.139,12.149l-0.001,0.0L8.996,12.149l0.0,-0.571L4.738,11.578l-0.002,2.198c0.0,0.589 0.477,1.066 1.066,1.066l8.535,0.0c0.589,0.0 1.066,-0.477 1.066,-1.066l0.0,-2.198l-4.264,0.0L11.139,12.149z" + android:fill="#FFFFFF"/> + <path + android:pathData="M8.996,10.006l2.143,0.0l0.0,0.52l4.442,0.0L15.580999,7.909c0.0,-0.589 -0.477,-1.066 -1.066,-1.066l-1.877,0.0L7.544,6.843L5.606,6.843c-0.589,0.0 -1.061,0.477 -1.061,1.066l-0.003,2.617l4.453,0.0L8.996,10.006L8.996,10.006z" + android:fill="#FFFFFF"/> + <path + android:pathData="M3.367,3.456 h13.016 v13.016 h-13.016z" + android:fill="#00000000"/> + <path + android:pathData="M7.368,5.263l5.263,0.0l0.0,1.053l-5.263,0.0z" + android:fill="#FFFFFF"/> + <path + android:pathData="M8.996,12.149l2.1419992,0.0 0.0010004044,0.0 0.0,-0.5699997 -2.1429996,0.0z" + android:fill="#00000000"/> +</vector> diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge.xml new file mode 100644 index 0000000..c8e49e1 --- /dev/null +++ b/core/res/res/drawable/ic_corp_icon_badge.xml @@ -0,0 +1,54 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64.0dp" + android:height="64.0dp"/> + + <viewport + android:viewportWidth="64.0" + android:viewportHeight="64.0"/> + + <path + android:fill="#FF000000" + android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:fillOpacity="0.2"/> + <path + android:fill="#FF000000" + android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:fillOpacity="0.2"/> + <path + android:pathData="M49.0,49.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:fill="#FF5722"/> + <path + android:pathData="M50.594,52.009l-3.0,0.0L47.594,51.0l-5.961,0.0l-0.003,3.289c0.0,0.826 0.668,1.494 1.494,1.494l11.948,0.0c0.826,0.0 1.494,-0.668 1.494,-1.494L56.566006,51.0l-5.972,0.0C50.594,51.0 50.594,52.009 50.594,52.009z" + android:fill="#FFFFFF"/> + <path + android:pathData="M47.594,49.009l3.0,0.0L50.594,50.0l6.22,0.0l0.0,-3.925c0.0,-0.826 -0.668,-1.494 -1.494,-1.494l-2.627,0.0l-7.131,-0.001l-2.713,0.0c-0.826,0.0 -1.486,0.668 -1.486,1.494L41.359,50.0l6.235,0.0L47.594,49.009z" + android:fill="#FFFFFF"/> + <path + android:pathData="M39.714,39.838 h18.221 v18.221 h-18.221z" + android:fill="#00000000"/> + <path + android:pathData="M47.594,49.009 h3.0 v0.991 h-3.0z" + android:fill="#00000000"/> + <path + android:pathData="M47.594,51.0 h3.0 v1.009 h-3.0z" + android:fill="#00000000"/> + <path + android:pathData="M46.0,43.0l6.0,0.0l0.0,1.0l-6.0,0.0z" + android:fill="#FFFFFF"/> +</vector> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index a8735cb..4e06d9a 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -814,6 +814,14 @@ via adb. The default value of this attribute is <code>true</code>. --> <attr name="allowBackup" format="boolean" /> + <!-- Indicates that even though the application provides a <code>BackupAgent</code>, + only full-data streaming backup operations are to be performed to save the app's + data. This lets the app rely on full-data backups while still participating in + the backup and restore process via the BackupAgent's full-data backup APIs. + When this attribute is <code>true</code> the app's BackupAgent overrides of + the onBackup() and onRestore() callbacks can be empty stubs. --> + <attr name="fullBackupOnly" format="boolean" /> + <!-- Whether the application in question should be terminated after its settings have been restored during a full-system restore operation. Single-package restore operations will never cause the application to @@ -1060,6 +1068,7 @@ <attr name="testOnly" /> <attr name="backupAgent" /> <attr name="allowBackup" /> + <attr name="fullBackupOnly" /> <attr name="killAfterRestore" /> <attr name="restoreNeedsApplication" /> <attr name="restoreAnyVersion" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a4f78bd..471eece 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1464,7 +1464,7 @@ <!-- Name of the CustomDialog that is used for VPN --> <string name="config_customVpnConfirmDialogComponent" - >com.android.vpndialogs/com.android.vpndialogs.CustomDialog</string> + >com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string> <!-- Apps that are authorized to access shared accounts, overridden by product overlays --> <string name="config_appsAuthorizedForSharedAccounts">;com.android.settings;</string> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 639091e..c64e910 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -84,4 +84,5 @@ <item type="id" name="current_scene" /> <item type="id" name="scene_layoutid_cache" /> <item type="id" name="mask" /> + <item type="id" name="transitionPosition" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index c5ce229..f6ad78b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2206,6 +2206,7 @@ <public type="attr" name="buttonTintMode" /> <public type="attr" name="thumbTint" /> <public type="attr" name="thumbTintMode" /> + <public type="attr" name="fullBackupOnly" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e31dbaf..c8c0d23 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4728,6 +4728,13 @@ <!-- Accessibility announcement when a number that had been typed in is deleted [CHAR_LIMIT=NONE] --> <string name="deleted_key"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string> + <!-- + Used to wrap a label for content description for a managed profile, e.g. "Work Email" instead + of email when there are two email apps. + [CHAR LIMIT=20] + --> + <string name="managed_profile_label_badge">Work <xliff:g id="label" example="Email">%1$s</xliff:g></string> + <!-- DO NOT TRANSLATE --> <string name="time_placeholder">--</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5f4553b..1547bbd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -218,6 +218,7 @@ <java-symbol type="id" name="pin_error_message" /> <java-symbol type="id" name="timePickerLayout" /> <java-symbol type="id" name="profile_icon" /> + <java-symbol type="id" name="transitionPosition" /> <java-symbol type="attr" name="actionModeShareDrawable" /> <java-symbol type="attr" name="alertDialogCenterButtons" /> @@ -863,6 +864,7 @@ <java-symbol type="string" name="action_bar_home_description_format" /> <java-symbol type="string" name="action_bar_home_subtitle_description_format" /> <java-symbol type="string" name="wireless_display_route_description" /> + <java-symbol type="string" name="managed_profile_label_badge" /> <java-symbol type="string" name="mediasize_unknown_portrait" /> <java-symbol type="string" name="mediasize_unknown_landscape" /> <java-symbol type="string" name="mediasize_iso_a0" /> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 7251e7c..7f41ac1c 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -305,11 +305,9 @@ public class PackageManagerTests extends AndroidTestCase { private PackageParser.Package parsePackage(Uri packageURI) throws PackageParserException { final String archiveFilePath = packageURI.getPath(); - PackageParser packageParser = new PackageParser(archiveFilePath); + PackageParser packageParser = new PackageParser(); File sourceFile = new File(archiveFilePath); - DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, 0); + PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, 0); packageParser = null; return pkg; } diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml index 1eaae65..37e9b15 100644 --- a/data/fonts/fallback_fonts.xml +++ b/data/fonts/fallback_fonts.xml @@ -219,6 +219,21 @@ </family> <family> <fileset> + <file>NotoSansCherokee-Regular.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>NotoSansCanadianAboriginal-Regular.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>NotoSansYi-Regular.ttf</file> + </fileset> + </family> + <family> + <fileset> <file lang="zh-CN">NotoSansHans-Regular.otf</file> </fileset> </family> diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd index b648d48..c4d5083 100644 --- a/docs/html/guide/topics/manifest/activity-element.jd +++ b/docs/html/guide/topics/manifest/activity-element.jd @@ -5,7 +5,8 @@ parent.link=manifest-intro.html <dl class="xml"> <dt>syntax:</dt> -<dd><pre class="stx"><activity android:<a href="#reparent">allowTaskReparenting</a>=["true" | "false"] +<dd><pre class="stx"><activity android:<a href="#embedded">allowEmbedded</a>=["true" | "false"] + android:<a href="#reparent">allowTaskReparenting</a>=["true" | "false"] android:<a href="#always">alwaysRetainTaskState</a>=["true" | "false"] android:<a href="#clear">clearTaskOnLaunch</a>=["true" | "false"] android:<a href="#config">configChanges</a>=["mcc", "mnc", "locale", @@ -62,6 +63,17 @@ by the system and will never be run. <dt>attributes:</dt> <dd><dl class="attr"> +<dt><a name="embedded"></a>{@code android:allowEmbedded}</dt> +<dd> + Indicate that the activity can be launched as the embedded child of another + activity. Particularly in the case where the child lives in a container + such as a Display owned by another activity. For example, activities + that are used for Wear custom notifications must declare this so + Wear can display the activity in it's context stream, which resides + in another process. + + <p>The default value of this attribute is <code>false</code>. +</dd> <dt><a name="reparent"></a>{@code android:allowTaskReparenting}</dt> <dd>Whether or not the activity can move from the task that started it to the task it has an affinity for when that task is next brought to the diff --git a/docs/html/images/tools/android-studio.png b/docs/html/images/tools/android-studio.png Binary files differdeleted file mode 100644 index 4d93a86..0000000 --- a/docs/html/images/tools/android-studio.png +++ /dev/null diff --git a/docs/html/images/tools/laptop-studio.png b/docs/html/images/tools/laptop-studio.png Binary files differnew file mode 100644 index 0000000..3684ff0 --- /dev/null +++ b/docs/html/images/tools/laptop-studio.png diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js index d8db5bf..2f63700 100644 --- a/docs/html/jd_extras.js +++ b/docs/html/jd_extras.js @@ -15,6 +15,17 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([ { + "title":"Android L Developer Preview", + "titleFriendly":"", + "summary":"<p style='font-size:18px;'>Get an early look at the next release and get your apps ready when the platform officially launches.</p>", + "url":"preview/index.html", + "group":"", + "keywords": [], + "tags": [], + "image":"preview/images/l-dev-prev.png", + "type":"" + }, + { "title":"Developer Registration", "titleFriendly":"", "summary":"Additional information about the registration process.", diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd index 2fa029f..8ec2470 100644 --- a/docs/html/preview/api-overview.jd +++ b/docs/html/preview/api-overview.jd @@ -18,7 +18,6 @@ sdk.platform.apiLevel=20 <li><a href="#ART">New Android Runtime (ART)</a></li> <li><a href="#BehaviorNotifications">If your app implements notifications...</a></li> <li><a href="#BehaviorMediaControl">If your app uses RemoteControlClient...</a></li> - <li><a href="#BehaviorFullscreen">If your app uses fullScreenIntent...</a></li> <li><a href="#BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</a></li> </ol> </li> @@ -64,12 +63,13 @@ sdk.platform.apiLevel=20 <li><a href="#Power">Power Efficiency</a> <ol> <li><a href="#JobScheduler">Scheduling Jobs</a></li> - <li><a href="#PowerMeasurementTools">Developer tools and APIs for power measurement</a> + <li><a href="#PowerMeasurementTools">Developer tools for power measurement</a> </ol> </li> <li><a href="#Enterprise">Enterprise</a> <ol> <li><a href="#ManagedProvisioning">Managed provisioning</a></li> + <li><a href="#LockToAppMode">Lock-to-App mode</a></li> </ol> </li> <li><a href="#Printing">Printing Framework</a> @@ -99,15 +99,15 @@ Differences Report »</a> </li> </div> </div> -<p>The L Developer Preview gives you an advance look at the upcoming release for -the Android platform, -which offers new features for users and app developers. This document provides -an introduction to the most notable APIs.</p> +<p>The L Developer Preview gives you an advance look at the upcoming release +for the Android platform, which offers new features for users and app +developers. This document provides an introduction to the most notable APIs.</p> -<p>The L Developer Preview is intended for <strong>developer early adopters</strong> and -<strong>testers</strong>. If you are interested in influencing the direction of the -Android framework, <a href="{@docRoot}preview/setup-sdk.html">give the L -Developer Preview a try</a> and send us your feedback!</p> +<p>The L Developer Preview is intended for <strong>developer early +adopters</strong> and <strong>testers</strong>. If you are interested in +influencing the direction of the Android framework, +<a href="{@docRoot}preview/setup-sdk.html">give the L Developer Preview a +try</a> and send us your feedback!</p> <p class="caution"><strong>Caution:</strong> Do not not publish apps that use the L Developer Preview to the Google Play store.</p> @@ -128,8 +128,8 @@ reference</a>.</p> <h3 id="ART">New Android Runtime (ART)</h3> <p>The 4.4 release introduced a new, experimental Android runtime, ART. Under -4.4, ART was optional, and the default runtime remained Dalvik. With the L Developer Preview, ART is -now the default runtime.</p> +4.4, ART was optional, and the default runtime remained Dalvik. With the L +Developer Preview, ART is now the default runtime.</p> <p>For an overview of ART's new features, see <a href="https://source.android.com/devices/tech/dalvik/art.html">Introducing @@ -163,6 +163,15 @@ Behavior on the Android Runtime (ART)</a>. Pay particular attention if:</p> backgrounds to match the new material design widgets. Make sure that all your notifications look right with the new color scheme:</p> +<div class="figure" style="width:220px"> + <img src="images/hun-example.png" + srcset="images/hun-example@2x.png 2x" + alt="" width="220" height="372" id="figure1" /> + <p class="img-caption"> + <strong>Figure 1.</strong> Fullscreen activity showing a heads-up notification + </p> +</div> + <ul> <li>Update or remove assets that involve color.</li> @@ -179,39 +188,48 @@ notifications look right with the new color scheme:</p> <p>If you are currently adding sounds and vibrations to your notifications by using the {@link android.media.Ringtone}, {@link android.media.MediaPlayer}, or {@link android.os.Vibrator} classes, remove this code so that -the system can present notifications correctly in <a href="#DoNotDisturb">Do Not Disturb</a> mode. -Instead, use the {@link android.app.Notification.Builder} methods instead to add -sounds and vibration.</p> +the system can present notifications correctly in <a href="#DoNotDisturb">Do Not Disturb</a> mode. Instead, use the {@link android.app.Notification.Builder} methods instead to add sounds and vibration.</p> + +<p>Notifications now appear in a small floating window +(also called a <em>heads-up notification</em>) when the device is active +(that is, the device is unlocked and its screen is on). These notifications +appear similar to the compact form of your notification, except that the +heads-up notification also shows action buttons. Users can act on, or dismiss, +a heads-up notification without leaving the current app.</p> + +<p>Examples of conditions that may trigger heads-up notifications include:</p> + +<ul> + <li>The user's activity is in fullscreen mode (the app uses +{@link android.app.Notification#fullScreenIntent}), or</li> + <li>The notification has high priority and uses ringtones or + vibrations</li> +</ul> + +<p>If your app implements notifications under those scenarios, make sure that +heads-up notifications are presented correctly.</p> <h3 id="BehaviorMediaControl">If your app uses RemoteControlClient...</h3> -<p>Lockscreens in the L Developer Preview do not show transport controls for your -{@link android.media.RemoteControlClient}. Instead, your app can provide -media playback control from the lockscreen through a media notification. This +<p>Lockscreens in the L Developer Preview do not show transport controls for +your {@link android.media.RemoteControlClient}. Instead, your app can provide +media playback control from the lockscreen through a notification. This gives your app more control over the presentation of media buttons, while providing a consistent experience for users across the lockscreen and unlocked device.</p> +<p>The L Developer Preview introduces a new {@code android.app.Notification.MediaStyle} template which is recommended for this purpose. {@code MediaStyle} converts notification actions that you added with {@link android.app.Notification.Builder#addAction(int, java.lang.CharSequence, android.app.PendingIntent) Notification.Builder.addAction()} into compact buttons embedded in your app's media playback notifications.</p> + +<p>If you are using the new +{@code android.media.session.MediaSession} class (see <a href="#MediaPlaybackControl">Media Playback Control</a> below), attach your session +token with {@code Notification.MediaStyle.setMediaToken()} to inform the +system that this notification controls an ongoing media session.</p> + <p>Call {@code Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark a -notification as safe to display on the lockscreen (even when the lockscreen is -secured with a PIN, pattern, or password). For more information, see +notification as safe to show atop any lockscreen (secure or otherwise). For more information, see <a href="#LockscreenNotifications">Lockscreen Notifications</a>.</p> -<h3 id="BehaviorFullscreen">If your app uses fullScreenIntent...</h3> - -<p>Notifications now appear in a small floating window if all these conditions -are met:</p> - -<ul> - <li>The user’s activity is in fullscreen mode,</li> - <li>The screen is on, and</li> - <li>The device is unlocked</li> -</ul> - -<p>If your app implements fullscreen activities, make sure that -these heads-up notifications are presented correctly.</p> - <h3 id="BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</h3> <p>With the introduction of the new <em>concurrent documents and activities tasks</em> feature in the upcoming @@ -230,7 +248,6 @@ information.</p> <h3 id="MaterialDesign">Material design support</h3> - <p>The upcoming release adds support for Android's new <em>material</em> design style. You can create apps with material design that are visually dynamic and have UI element transitions @@ -262,16 +279,17 @@ values:</p> <ul> <li>{@code VISIBILITY_PRIVATE}. Shows basic information, such as the -notification’s icon, but hides the notification’s full content. If you want to -provide a redacted public version of your notification for the system to display -on a secure lockscreen, create a public notification object and put a reference -to it in the private notification's {@code publicVersion} field.</li> -<li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content. This is - the system default if visibility is left unspecified.</li> -<li>{@code VISIBILITY_SECRET}. Shows only the most minimal information, -excluding even the notification’s icon.</li> +notification’s icon, but hides the notification’s full content.</li> +<li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content.</li> +<li>{@code VISIBILITY_SECRET}. Shows nothing, excluding even the +notification’s icon.</li> </ul> +<p>When {@code VISIBILITY_PRIVATE} is set, you can also provide a redacted +version of the notification content that hides personal details. For example, +an SMS app might display a notification that shows "You have 3 new text messages." but hides the message content and senders. To provide this alternative notification, first create the replacement notification using {@link android.app.Notification.Builder}. When you create the private notification object, attach +the replacement notification to it through the {@code Notification.Builder.setPublicVersion()} method.</p> + <h3 id="DoNotDisturb">Do Not Disturb mode</h3> <p>The L Developer Preview introduces a new <em>Do Not Disturb</em> mode. When @@ -295,7 +313,7 @@ make sure <em>Do Not Disturb</em> mode handles them properly. For example, if your app is an alarm clock, you can tag the notification as an alarm so it will wake the user up even if the device is in <em>Do Not Disturb</em> mode. For more information, see <a -href="NotificationsMetadata">Notifications metadata</a>.</p> +href="#NotificationsMetadata">Notifications metadata</a>.</p> <h3 id="NotificationsMetadata">Notifications metadata</h3> <p>The L Developer Preview uses metadata associated with your app notifications @@ -483,10 +501,9 @@ knows about your playback and can extract and show album art.</p> <h3 id="DirectorySelection">Directory selection</h3> -<p>The L Developer Preview extends the <a href="{@docRoot}guide/topics/providers/document-provider.html">Storage Access Framework</a> to let users -select an entire directory, rather than individual files, to give your app -read/write access to media files. When a directory is selected, your app also -has access to all its child directories and content.</p> +<p>The L Developer Preview extends the <a href="{@docRoot}guide/topics/providers/document-provider.html">Storage Access Framework</a> to let users select an entire directory, rather than individual files, to +give your app read/write access to media files. When a directory is selected, +your app also has access to all its child directories and content.</p> <p>To get the absolute paths to directories on external storage devices where applications can store media files, call the new @@ -498,6 +515,13 @@ considers to be a permanent part of the device, and includes emulated external storage and physical media slots such as SD cards in battery compartments.</p> +<p>You can bring up a system UI to allow the user to pick a directory subtree. +To do so, send {@code android.intent.action.OPEN_DOCUMENT_TREE} in an +{@link android.content.Intent}. If the call is successful, the system displays +the {@link android.provider.DocumentsProvider} instances installed on the +device for the user to select. When the user selects a directory from this UI, +the system returns a URI representing the selected directory tree.</p> + <p>If you want to access a document in an existing directory, call the {@code android.provider.DocumentsContract.buildDocumentViaUri()} method. Pass the method a URI representing the path to the parent directory, and the @@ -591,9 +615,9 @@ API that lets you optimize battery life by defining jobs for the system to run asynchronously at a later time or under specified conditions (such as when the device is charging). This is useful in such situations as:</p> <ul> - <li>The app has non-user-facing work that you want to defer until the unit is - plugged in.</li> - <li>The app has a task that requires network access (or requires a wifi + <li>The app has non-user-facing work that you can defer.</li> + <li>The app has work you'd prefer to do when the unit is plugged in.</li> + <li>The app has a task that requires network access (or requires a Wi-Fi connection).</li> <li>The app has a number of tasks that you want to run as a batch on a regular schedule.</li> @@ -612,6 +636,7 @@ conditions, such as:</p> <li>The device is charging</li> <li>The device is connected to an unmetered network</li> <li>The system deems the device to be idle</li> + <li>Completion with a minimum delay or within a specific deadline.</li> </ul> <p>For example, you can add code like this to run your task on an @@ -627,7 +652,7 @@ JobScheduler jobScheduler = jobScheduler.schedule(uploadTask); </pre> -<h3 id="PowerMeasurementTools">Developer tools and APIs for power measurement</h3> +<h3 id="PowerMeasurementTools">Developer tools for power measurement</h3> <p>The L Developer Preview provides several new developer tools and APIs to help you better measure and understand your app's power usage.</p> @@ -668,9 +693,9 @@ in {@code <sdk>/tools}.</p> <img src="images/battery_historian.png" srcset="images/battery_historian@2x.png 2x" alt="" width="440" height="240" - id="figure1" /> + id="figure2" /> <p class="img-caption"> - <strong>Figure 1.</strong>HTML visualization generated by the Battery + <strong>Figure 2.</strong>HTML visualization generated by the Battery Historian tool. </p> @@ -701,9 +726,9 @@ $ historian.par [-p powerfile] bugreport.txt > out.html <div class="figure" style="width:360px"> <img src="images/managed_apps_launcher.png" srcset="images/managed_apps_launcher@2x.png 2x" - alt="" width="360" height="572" id="figure2" /> + alt="" width="360" height="572" id="figure3" /> <p class="img-caption"> - <strong>Figure 2.</strong> Launcher screen showing managed apps (marked with + <strong>Figure 3.</strong> Launcher screen showing managed apps (marked with a lock badge) </p> </div> @@ -742,6 +767,36 @@ for the current user and any associated managed profiles. Your Launcher can make the managed apps visually prominent by appending a “work” badge to the icon drawable with {@code android.os.UserManager.getBadgeDrawableForUser()}.</p> +<h3 id="LockToAppMode">Lock-to-App mode</h3> +<p>The L Developer Preview introduces a new <em>Lock-to-App</em> mode that +lets you temporarily restrict users from leaving your app or being interrupted +by notifications. Once your app activates this mode, users will not be able to +see notifications, access other apps, or return to the Home screen, until your +app exits the mode.</p> + +<p>To prevent unauthorized usage, the device on which you want to activate +this mode must have managed profiles or must be fully controlled by a device administrator (see <a href="#ManagedProvisioning">Managed Provisioning</a> for more information). Furthermore, the device or managed profile owner must +authorize apps to use this mode by calling {@code android.app.admin.DevicePolicyManager.setLockTaskComponents()}.</p> + +<p>Before activating this mode in your app, verify that your activity is authorized by calling {@code DevicePolicyManager.isLockTaskPermitted()}.</p> + +<p>To activate <em>Lock-to-App</em> mode, call +{@code android.app.Activity.startLockTask()} from your authorized activity.</p> + +<p>When <em>Lock-to-App</em> mode is active, the following behavior takes +effect:</p> + +<ul> +<li>The status bar is blank, and user notifications and status information is hidden.</li> +<li>The Home and Recent Apps button is hidden.</li> +<li>Other apps may not launch new activities.</li> +<li>The current app may start new activities, as long as doing so does not +create new tasks.</li> +</ul> + +<p>The device will remain in this mode until an authorized activity calls +{@code Activity.stopLockTask()}. + <h2 id="Printing">Printing Framework</h2> <h3 id="PDFRender">Render PDF as bitmap</h3> @@ -752,7 +807,7 @@ accessed) on which the system writes the the printable content. Your app can obtain a page for rendering with {@code openPage()}, then call {@code render()} to turn the opened {@code PdfRenderer.Page} into a bitmap. You can also set additional parameters if you only want to convert a portion of the document into -a bitmap image (for example, to implement <a href="http://en.wikipedia.org/wiki/Tiled_rendering">tile rendering</a> in order to zoom in on the document).</p> +a bitmap image (for example, to implement <a href="http://en.wikipedia.org/wiki/Tiled_rendering">tiled rendering</a> in order to zoom in on the document).</p> <h2 id="TestingA11y">Testing & Accessibility </h2> @@ -796,8 +851,7 @@ your app needs.</p> <ul> <li>{@code FEATURE_LEANBACK}. Declares that your app must be installed only on -devices that support the <a href="{@docRoot}training/tv}">Android TV</a> user -interface. Example: +devices that support the <a href="{@docRoot}training/tv/index.html}">Android TV</a>user interface. Example: <pre> <uses-feature android:name="android.software.leanback" android:required="true" /> diff --git a/docs/html/preview/images/hun-example.png b/docs/html/preview/images/hun-example.png Binary files differnew file mode 100644 index 0000000..9613a92 --- /dev/null +++ b/docs/html/preview/images/hun-example.png diff --git a/docs/html/preview/images/hun-example@2x.png b/docs/html/preview/images/hun-example@2x.png Binary files differnew file mode 100644 index 0000000..3cb8f5b --- /dev/null +++ b/docs/html/preview/images/hun-example@2x.png diff --git a/docs/html/preview/images/l-dev-prev.png b/docs/html/preview/images/l-dev-prev.png Binary files differnew file mode 100644 index 0000000..95bad8c --- /dev/null +++ b/docs/html/preview/images/l-dev-prev.png diff --git a/docs/html/preview/images/managed_apps_launcher@2.png b/docs/html/preview/images/managed_apps_launcher@2x.png Binary files differindex d298fd2..d298fd2 100644 --- a/docs/html/preview/images/managed_apps_launcher@2.png +++ b/docs/html/preview/images/managed_apps_launcher@2x.png diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.html index e44e9f3..368db84 100644 --- a/docs/html/preview/index.jd +++ b/docs/html/preview/index.html @@ -1,13 +1,143 @@ -page.title=Android L Developer Preview -page.viewport_width=970 -fullpage=true -no_footer_links=true -page.type=about -page.metaDescription=Test and build your apps against the next version of Android to ensure they're ready when the platform officially launches. -page.image={@docRoot}preview/images/hero.jpg -@jd:body +<!DOCTYPE html> -<style> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<html> +<head> + + +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="viewport" content="width=970" /> + +<meta name="Description" content="Test and build your apps against the next version of Android to ensure they're ready when the platform officially launches."> +<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /> +<title>Android L Developer Preview | Android Developers</title> + +<!-- STYLESHEETS --> +<link rel="stylesheet" +href="//fonts.googleapis.com/css?family=Roboto+Condensed"> +<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:light,regular,medium,thin,italic,mediumitalic,bold" + title="roboto"> +<link href="/assets/css/default.css" rel="stylesheet" type="text/css"> + + + +<!-- JAVASCRIPT --> +<script src="//www.google.com/jsapi" type="text/javascript"></script> +<script src="/assets/js/android_3p-bundle.js" type="text/javascript"></script> +<script type="text/javascript"> + var toRoot = "/"; + var metaTags = []; + var devsite = false; +</script> +<script src="/assets/js/docs.js" type="text/javascript"></script> + +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-5831155-1', 'android.com'); + ga('create', 'UA-49880327-2', 'android.com', {'name': 'universal'}); // New tracker); + ga('send', 'pageview'); + ga('universal.send', 'pageview'); // Send page view for new tracker. +</script> + +</head> + +<body class="gc-documentation + +" itemscope itemtype="http://schema.org/Article"> + + +<a name="top"></a> +<div id="body-content"> +<div class="fullpage" > +<div id="jd-content"> + <div class="jd-descr" itemprop="articleBody"> + <style> .fullpage>#footer, #jd-content>.content-footer.wrap { display:none; @@ -23,61 +153,36 @@ page.image={@docRoot}preview/images/hero.jpg } </style> -<div class="landing-body-content"> - <div class="landing-hero-container"> - <div class="landing-section preview-hero"> - <div class="landing-hero-scrim"></div> - <div class="landing-hero-wrap"> - <div class="vertical-center-outer"> - <div class="vertical-center-inner"> - - <div class="col-12"> - <div class="landing-section-header"> - - <div class="landing-h1 hero">L Developer Preview</div> - <div class="landing-subhead hero"> - <p>An early look at the next release</p> - </div> - <div class="landing-hero-description"> - <p>Test and build your apps against the next<br /> - version of Android to ensure they're ready<br/> - when the platform officially launches.</p> - </div> - - <div class="landing-body"> - <a href="/preview/setup-sdk.html" class="landing-button landing-primary" style="margin-top: 40px;"> - Get Started - </a> - </div> - </div> - - </div> - </div> - </div> <!-- end .wrap --> - <div class="landing-scroll-down-affordance"> - <a class="landing-down-arrow" href="#extending-android-to-landingables"> - <img src="/wear/images/carrot.png" alt="Scroll down to read more"> - </a> - </div> - </div> <!-- end .landing-section .landing-hero --> - </div> <!-- end .landing-hero-container --> - - <div class="landing-rest-of-page"> - <div class="landing-section" id="extending-android-to-landingables"> + <div class="landing-section" style="padding-top:30px"> <div class="wrap"> <div class="landing-section-header"> - <div class="landing-h1">See What's New</div> + <div class="landing-h1">Android L Developer Preview</div> <div class="landing-subhead"> - Take advantage of all the new capabilities, which are focused on design and performance. + Get an early look at the next release and get your apps ready when the + platform officially launches. + </div> + + <img src="/preview/images/l-dev-prev.png" style=" margin:10px 0 0 100px" width="700px"/> + <div class="col-6" style="margin-left:630px; margin-top:-40px"> + <a href="/preview/setup-sdk.html" class="landing-button landing-secondary" style="position:absolute;z-index:100;float:right;margin-top: 0px; background-color:#09c">Get Started</a><!-- + <p>Set up your environment and check out all the docs to get up and running.</p>--> + + </div> </div> + </div> <!-- end .wrap --> + </div> <!-- end .landing-section --> - <div class="landing-body"> + + +<div class="landing-section landing-gray-background" style="margin-top:-80px; padding-bottom:20px"> + <div class="wrap"> + <div class="cols"> +<div class="landing-body" style="margin-top:-80px" > <div class="landing-breakout cols"> <div class="col-4"> - <img src="/preview/images/material.png" style="opacity:.6" alt=""> <p>A New UI Design</p> <p class="landing-small"> Create a consistent experience across mobile and the web with @@ -88,19 +193,16 @@ page.image={@docRoot}preview/images/hero.jpg </p> </div> <div class="col-4"> - <img src="/preview/images/art.png" alt=""> <p>A Rehauled Runtime</p> <p class="landing-small"> Test your apps and get them ready for <b>ART</b> (<b>A</b>ndroid <b>R</b>un<b>t</b>ime), the default runtime in the next release. - </p> <p class="landing-small"> <a href="/preview/api-overview.html#ART">Learn about ART</a> </p> </div> <div class="col-4"> - <img src="/preview/images/notifications.png" alt=""> <p style="width:230px">Enhanced Notifications</p> <p class="landing-small"> Get more control over where notifications appear, @@ -111,7 +213,6 @@ page.image={@docRoot}preview/images/hero.jpg </p> </div> <div class="col-4"> - <img src="/preview/images/volta.png" alt=""> <p>Project Volta</p> <p class="landing-small"> We've tuned the platform to be more energy efficient and @@ -122,44 +223,17 @@ page.image={@docRoot}preview/images/hero.jpg </p> </div> </div> - <p>See the <a href="{@docRoot}preview/api-overview.html">API overview</a> for more information + <p style="margin-left:20px">See the <a href="/preview/api-overview.html">API overview</a> for more information on the rest of the new and updated features.</p> </div> - </div> <!-- end .wrap --> - </div> <!-- end .landing-section --> - - - - <div class="landing-section landing-gray-background"> - <div class="wrap"> - <div class="landing-section-header"> - <div class="landing-h1">Get Your Apps Ready</div> - <div class="landing-subhead"> - <p>We're giving you an early look at the SDK, so you can test your apps and build in new features.</p> - </div> - </div> - <div class="landing-body"> - <p>You'll get the system images for the Nexus 5, Nexus 7 (v2), - and the emulator to take the new platform for a spin. In addition, you'll have - access to all the APIs with a preview build of the SDK. - </p> - - <p>Check out the getting started, developer guides, and reference documentation - for all the information you need to get up and running.</p> - - <a href="/preview/setup-sdk.html" class="landing-button landing-secondary" style="margin-top: 20px;"> - Get Started - </a> - </div> - </div> - </div> + </div></div></div> <div class="landing-section"> <div class="wrap"> <div class="cols"> <div class="landing-body"> <div class="col-3-wide"> - <a target="_blank" href="http://submit-bugs!"> - <img class="landing-social-image" src="{@docRoot}preview/images/bugs.png" alt=""> + <a target="_blank" href="https://code.google.com/p/android-developer-preview/"> + <img class="landing-social-image" src="/preview/images/bugs.png" alt=""> </a> <div class="landing-social-copy"> <p>Issue Tracker</p> @@ -167,7 +241,7 @@ page.image={@docRoot}preview/images/hero.jpg Let us know when you encounter problems, so we can fix them and make the platform better for you and your users. </p><p class="landing-small"> - <a target="_blank" href="http://submit-bugs!"> + <a target="_blank" href="https://code.google.com/p/android-developer-preview/"> Report Issues</a> </p> <p></p> @@ -189,8 +263,8 @@ page.image={@docRoot}preview/images/hero.jpg </div> </div> <div class="col-3-wide"> - <a target="_blank" href="{@docRoot}preview/release-notes.html"> - <img class="landing-social-image" src="{@docRoot}preview/images/updates.png" alt=""> + <a target="_blank" href="/preview/support.html"> + <img class="landing-social-image" src="/preview/images/updates.png" alt=""> </a> <div class="landing-social-copy"> <p>Support and Updates</p> @@ -200,13 +274,13 @@ page.image={@docRoot}preview/images/hero.jpg for news about the changes. </p> <p class="landing-small"> - <a target="_blank" href="{@docRoot}preview/support.html">Get Support</a> + <a target="_blank" href="/preview/support.html">Get Support</a> </p> </div> </div> </div> </div> - </div> <!-- end .wrap --> + </div> </div> <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement"> @@ -233,4 +307,55 @@ page.image={@docRoot}preview/images/hero.jpg }, 1000, "easeOutQuint"); e.preventDefault(); }); - </script>
\ No newline at end of file + </script> + </div> + + <div class="content-footer wrap" + itemscope itemtype="http://schema.org/SiteNavigationElement"> + + <div class="paging-links layout-content-col col-10"> + + </div> + <div class="layout-content-col plus-container col-2" > + <style>#___plusone_0 {float:right !important;}</style> + <div class="g-plusone" data-size="medium"></div> + + </div> + + </div> + + + + + </div> <!-- end jd-content --> + +<div id="footer" class="wrap" style="width:940px"> + + + <div id="copyright"> + + Except as noted, this content is + licensed under <a href="http://creativecommons.org/licenses/by/2.5/"> + Creative Commons Attribution 2.5</a>. For details and + restrictions, see the <a href="/license.html">Content + License</a>. + </div> + + +</div> <!-- end footer --> +</div><!-- end doc-content --> + +</div> <!-- end body-content --> + + + + + + <script src="https://developer.android.com/ytblogger_lists_unified.js" type="text/javascript"></script> + <script src="/jd_lists_unified.js" type="text/javascript"></script> + <script src="/jd_extras.js" type="text/javascript"></script> + <script src="/jd_collections.js" type="text/javascript"></script> + <script src="/jd_tag_helpers.js" type="text/javascript"></script> + +</body> +</html>
\ No newline at end of file diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd index 8ac6163..894514a 100644 --- a/docs/html/sdk/installing/studio.jd +++ b/docs/html/sdk/installing/studio.jd @@ -186,7 +186,7 @@ This is the Android Software Development Kit License Agreement <div id="main"> <div class="figure" style="width:400px;margin-top:-75px"> -<img src="{@docRoot}images/tools/android-studio.png" height="330" width="400" style="margin-bottom:20px" /> +<img src="{@docRoot}images/tools/laptop-studio.png" height="366" width="400" style="margin-bottom:20px" /> <a class="big button subtitle" id="download-ide-button" href="" style="display:none;width:368px;margin:0 auto;display:block;font-size:18px" ></a> @@ -218,7 +218,7 @@ capabilities you expect from IntelliJ, Android Studio offers:</p> <li>Lint tools to catch performance, usability, version compatibility, and other problems.</li> <li>ProGuard and app-signing capabilities.</li> <li>Built-in support for <a - href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html" + href="https://developers.google.com/cloud/devtools/android_studio_templates/" class="external-link">Google Cloud Platform</a>, making it easy to integrate Google Cloud Messaging and App Engine. </ul> diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 158801c..13421aa 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -1770,7 +1770,7 @@ public class Canvas { } native_drawTextOnPath(mNativeCanvasWrapper, text, index, count, path.ni(), hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } /** @@ -1790,7 +1790,7 @@ public class Canvas { float vOffset, @NonNull Paint paint) { if (text.length() > 0) { native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } } @@ -2021,11 +2021,11 @@ public class Canvas { int count, long nativePath, float hOffset, float vOffset, int bidiFlags, - long nativePaint); + long nativePaint, long nativeTypeface); private static native void native_drawTextOnPath(long nativeCanvas, String text, long nativePath, float hOffset, float vOffset, - int flags, long nativePaint); + int flags, long nativePaint, long nativeTypeface); private static native void finalizer(long nativeCanvas); } diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java index a021165..5aa7c6a 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -31,18 +31,13 @@ public class Picture { private Canvas mRecordingCanvas; private final long mNativePicture; - /** - * @hide - */ - public final boolean createdFromStream; - private static final int WORKING_STREAM_STORAGE = 16 * 1024; /** * Creates an empty picture that is ready to record. */ public Picture() { - this(nativeConstructor(0), false); + this(nativeConstructor(0)); } /** @@ -51,7 +46,23 @@ public class Picture { * changes will not be reflected in this picture. */ public Picture(Picture src) { - this(nativeConstructor(src != null ? src.mNativePicture : 0), false); + this(nativeConstructor(src != null ? src.mNativePicture : 0)); + } + + private Picture(long nativePicture) { + if (nativePicture == 0) { + throw new RuntimeException(); + } + mNativePicture = nativePicture; + } + + @Override + protected void finalize() throws Throwable { + try { + nativeDestructor(mNativePicture); + } finally { + super.finalize(); + } } /** @@ -85,13 +96,17 @@ public class Picture { * Get the width of the picture as passed to beginRecording. This * does not reflect (per se) the content of the picture. */ - public native int getWidth(); + public int getWidth() { + return nativeGetWidth(mNativePicture); + } /** * Get the height of the picture as passed to beginRecording. This * does not reflect (per se) the content of the picture. */ - public native int getHeight(); + public int getHeight() { + return nativeGetHeight(mNativePicture); + } /** * Draw this picture on the canvas. @@ -130,7 +145,7 @@ public class Picture { */ @Deprecated public static Picture createFromStream(InputStream stream) { - return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]), true); + return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE])); } /** @@ -159,32 +174,12 @@ public class Picture { } } - protected void finalize() throws Throwable { - try { - nativeDestructor(mNativePicture); - } finally { - super.finalize(); - } - } - - final long ni() { - return mNativePicture; - } - - private Picture(long nativePicture, boolean fromStream) { - if (nativePicture == 0) { - throw new RuntimeException(); - } - mNativePicture = nativePicture; - createdFromStream = fromStream; - } - // return empty picture if src is 0, or a copy of the native src private static native long nativeConstructor(long nativeSrcOr0); - private static native long nativeCreateFromStream(InputStream stream, - byte[] storage); - private static native long nativeBeginRecording(long nativeCanvas, - int w, int h); + private static native long nativeCreateFromStream(InputStream stream, byte[] storage); + private static native int nativeGetWidth(long nativePicture); + private static native int nativeGetHeight(long nativePicture); + private static native long nativeBeginRecording(long nativeCanvas, int w, int h); private static native void nativeEndRecording(long nativeCanvas); private static native void nativeDraw(long nativeCanvas, long nativePicture); private static native boolean nativeWriteToStream(long nativePicture, @@ -201,18 +196,15 @@ public class Picture { @Override public void setBitmap(Bitmap bitmap) { - throw new RuntimeException( - "Cannot call setBitmap on a picture canvas"); + throw new RuntimeException("Cannot call setBitmap on a picture canvas"); } @Override public void drawPicture(Picture picture) { if (mPicture == picture) { - throw new RuntimeException( - "Cannot draw a picture into its recording canvas"); + throw new RuntimeException("Cannot draw a picture into its recording canvas"); } super.drawPicture(picture); } } } - diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 8be6eb7..38b8aaf 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -65,8 +65,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { private long mExitAnimationEnd; private Drawable mLastDrawable; - private Insets mInsets = Insets.NONE; - // overrides from Drawable @Override @@ -118,7 +116,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { */ @Override public Insets getOpticalInsets() { - return mInsets; + if (mCurrDrawable != null) { + return mCurrDrawable.getOpticalInsets(); + } + return Insets.NONE; } @Override @@ -203,9 +204,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } if (mCurrDrawable != null) { mCurrDrawable.setBounds(bounds); - - // Must obtain optical insets after setting bounds. - mInsets = mCurrDrawable.getOpticalInsets(); } } @@ -422,15 +420,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { d.setBounds(getBounds()); d.setLayoutDirection(getLayoutDirection()); d.setAutoMirrored(mDrawableContainerState.mAutoMirrored); - - // Must obtain optical insets after setting bounds. - mInsets = d.getOpticalInsets(); - } else { - mInsets = Insets.NONE; } } else { mCurrDrawable = null; - mInsets = Insets.NONE; mCurIndex = -1; } diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java index 345400e..2d49365 100644 --- a/graphics/java/android/graphics/drawable/Ripple.java +++ b/graphics/java/android/graphics/drawable/Ripple.java @@ -142,14 +142,16 @@ class Ripple { } private void clampStartingPosition() { - final float dX = mStartingX - mBounds.exactCenterX(); - final float dY = mStartingY - mBounds.exactCenterY(); + final float cX = mBounds.exactCenterX(); + final float cY = mBounds.exactCenterY(); + final float dX = mStartingX - cX; + final float dY = mStartingY - cY; final float r = mOuterRadius; if (dX * dX + dY * dY > r * r) { // Point is outside the circle, clamp to the circumference. final double angle = Math.atan2(dY, dX); - mClampedStartingX = (float) (Math.cos(angle) * r); - mClampedStartingY = (float) (Math.sin(angle) * r); + mClampedStartingX = cX + (float) (Math.cos(angle) * r); + mClampedStartingY = cY + (float) (Math.sin(angle) * r); } else { mClampedStartingX = mStartingX; mClampedStartingY = mStartingY; diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index c3c1bca..a1e1f76 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -147,10 +147,12 @@ public class VectorDrawable extends Drawable { } private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) { - mVectorState = new VectorDrawableState(state); - - if (theme != null && canApplyTheme()) { + if (theme != null && state.canApplyTheme()) { + // If we need to apply a theme, implicitly mutate. + mVectorState = new VectorDrawableState(state); applyTheme(theme); + } else { + mVectorState = state; } mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode); diff --git a/include/androidfw/ByteBucketArray.h b/include/androidfw/ByteBucketArray.h new file mode 100644 index 0000000..87c6b12 --- /dev/null +++ b/include/androidfw/ByteBucketArray.h @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#ifndef __BYTE_BUCKET_ARRAY_H +#define __BYTE_BUCKET_ARRAY_H + +#include <utils/Log.h> +#include <stdint.h> +#include <string.h> + +namespace android { + +/** + * Stores a sparsely populated array. Has a fixed size of 256 + * (number of entries that a byte can represent). + */ +template<typename T> +class ByteBucketArray { +public: + ByteBucketArray() : mDefault() { + memset(mBuckets, 0, sizeof(mBuckets)); + } + + ~ByteBucketArray() { + for (size_t i = 0; i < NUM_BUCKETS; i++) { + if (mBuckets[i] != NULL) { + delete [] mBuckets[i]; + } + } + memset(mBuckets, 0, sizeof(mBuckets)); + } + + inline size_t size() const { + return NUM_BUCKETS * BUCKET_SIZE; + } + + inline const T& get(size_t index) const { + return (*this)[index]; + } + + const T& operator[](size_t index) const { + if (index >= size()) { + return mDefault; + } + + uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4; + T* bucket = mBuckets[bucketIndex]; + if (bucket == NULL) { + return mDefault; + } + return bucket[0x0f & static_cast<uint8_t>(index)]; + } + + T& editItemAt(size_t index) { + ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u", + (uint32_t) index, (uint32_t) size()); + + uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4; + T* bucket = mBuckets[bucketIndex]; + if (bucket == NULL) { + bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE](); + } + return bucket[0x0f & static_cast<uint8_t>(index)]; + } + + bool set(size_t index, const T& value) { + if (index >= size()) { + return false; + } + + editItemAt(index) = value; + return true; + } + +private: + enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 }; + + T* mBuckets[NUM_BUCKETS]; + T mDefault; +}; + +} // namespace android + +#endif // __BYTE_BUCKET_ARRAY_H diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index 4d8e512..e612c0a 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -237,6 +237,7 @@ enum { #define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) #define Res_MAXPACKAGE 255 +#define Res_MAXTYPE 255 /** * Representation of a value in a resource, supplying type @@ -510,6 +511,23 @@ private: uint32_t mStylePoolSize; // number of uint32_t }; +/** + * Wrapper class that allows the caller to retrieve a string from + * a string pool without knowing which string pool to look. + */ +class StringPoolRef { +public: + StringPoolRef(); + StringPoolRef(const ResStringPool* pool, uint32_t index); + + const char* string8(size_t* outLen) const; + const char16_t* string16(size_t* outLen) const; + +private: + const ResStringPool* mPool; + uint32_t mIndex; +}; + /** ******************************************************************** * XML Tree * @@ -835,6 +853,8 @@ struct ResTable_package // Last index into keyStrings that is for public use by others. uint32_t lastPublicKey; + + uint32_t typeIdOffset; }; // The most specific locale can consist of: @@ -1469,9 +1489,13 @@ public: bool copyData=false); ~ResTable(); - status_t add(Asset* asset, const int32_t cookie, bool copyData, - const void* idmap = NULL); - status_t add(const void *data, size_t size); + status_t add(const void* data, size_t size, const int32_t cookie=-1, bool copyData=false); + status_t add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, + const int32_t cookie=-1, bool copyData=false); + + status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false); + status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false); + status_t add(ResTable* src); status_t addEmpty(const int32_t cookie); @@ -1610,13 +1634,14 @@ public: uint32_t typeSpecFlags; Res_value value; }; + struct type_info { size_t numEntries; theme_entry* entries; }; + struct package_info { - size_t numTypes; - type_info types[]; + type_info types[Res_MAXTYPE + 1]; }; void free_package(package_info* pi); @@ -1711,6 +1736,7 @@ public: size_t getBasePackageCount() const; const String16 getBasePackageName(size_t idx) const; uint32_t getBasePackageId(size_t idx) const; + uint32_t getLastTypeIdForPackage(size_t idx) const; // Return the number of resource tables that the object contains. size_t getTableCount() const; @@ -1740,13 +1766,15 @@ public: void** outData, size_t* outSize) const; enum { - IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t) + 2 * 256, + IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256, }; + // Retrieve idmap meta-data. // // This function only requires the idmap header (the first // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file. static bool getIdmapInfo(const void* idmap, size_t size, + uint32_t* pVersion, uint32_t* pTargetCrc, uint32_t* pOverlayCrc, String8* pTargetPath, String8* pOverlayPath); @@ -1756,21 +1784,24 @@ public: private: struct Header; struct Type; + struct Entry; struct Package; struct PackageGroup; struct bag_set; + typedef Vector<Type*> TypeList; - status_t addInternal(const void* data, size_t size, const int32_t cookie, - bool copyData, const Asset* idmap); + status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, + const int32_t cookie, bool copyData); ssize_t getResourcePackageIndex(uint32_t resID) const; - ssize_t getEntry( - const Package* package, int typeIndex, int entryIndex, + + status_t getEntry( + const PackageGroup* packageGroup, int typeIndex, int entryIndex, const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const; + Entry* outEntry) const; + status_t parsePackage( - const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id); + const ResTable_package* const pkg, const Header* const header); void print_value(const Package* pkg, const Res_value& value) const; diff --git a/include/androidfw/TypeWrappers.h b/include/androidfw/TypeWrappers.h new file mode 100644 index 0000000..7bdf8af --- /dev/null +++ b/include/androidfw/TypeWrappers.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef __TYPE_WRAPPERS_H +#define __TYPE_WRAPPERS_H + +#include <androidfw/ResourceTypes.h> + +namespace android { + +struct TypeVariant { + TypeVariant(const ResTable_type* data) + : data(data) {} + + class iterator { + public: + iterator& operator=(const iterator& rhs) { + mTypeVariant = rhs.mTypeVariant; + mIndex = rhs.mIndex; + } + + bool operator==(const iterator& rhs) const { + return mTypeVariant == rhs.mTypeVariant && mIndex == rhs.mIndex; + } + + bool operator!=(const iterator& rhs) const { + return mTypeVariant != rhs.mTypeVariant || mIndex != rhs.mIndex; + } + + iterator operator++(int) { + uint32_t prevIndex = mIndex; + operator++(); + return iterator(mTypeVariant, prevIndex); + } + + const ResTable_entry* operator->() const { + return operator*(); + } + + uint32_t index() const { + return mIndex; + } + + iterator& operator++(); + const ResTable_entry* operator*() const; + + private: + friend struct TypeVariant; + iterator(const TypeVariant* tv, uint32_t index) + : mTypeVariant(tv), mIndex(index) {} + const TypeVariant* mTypeVariant; + uint32_t mIndex; + }; + + iterator beginEntries() const { + return iterator(this, 0); + } + + iterator endEntries() const { + return iterator(this, dtohl(data->entryCount)); + } + + const ResTable_type* data; +}; + +} // namespace android + +#endif // __TYPE_WRAPPERS_H diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 9d6d76e..0da2b99 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -23,7 +23,9 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.security.InvalidKeyException; @@ -437,6 +439,14 @@ public final class KeyChain { * Caller should call unbindService on the result when finished. */ public static KeyChainConnection bind(Context context) throws InterruptedException { + return bindAsUser(context, Process.myUserHandle()); + } + + /** + * @hide + */ + public static KeyChainConnection bindAsUser(Context context, UserHandle user) + throws InterruptedException { if (context == null) { throw new NullPointerException("context == null"); } @@ -459,9 +469,10 @@ public final class KeyChain { Intent intent = new Intent(IKeyChainService.class.getName()); ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); intent.setComponent(comp); - boolean isBound = context.bindService(intent, - keyChainServiceConnection, - Context.BIND_AUTO_CREATE); + boolean isBound = context.bindServiceAsUser(intent, + keyChainServiceConnection, + Context.BIND_AUTO_CREATE, + user); if (!isBound) { throw new AssertionError("could not bind to KeyChainService"); } diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk index d21197e..957809d 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -25,6 +25,7 @@ commonSources := \ ObbFile.cpp \ ResourceTypes.cpp \ StreamingZipInflater.cpp \ + TypeWrappers.cpp \ ZipFileRO.cpp \ ZipUtils.cpp diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 1b3f1fd..0340928 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -256,7 +256,7 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie) String8 targetPath; String8 overlayPath; if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(), - NULL, NULL, &targetPath, &overlayPath)) { + NULL, NULL, NULL, &targetPath, &overlayPath)) { ALOGW("failed to read idmap file %s\n", idmapPath.string()); delete idmap; return false; @@ -311,7 +311,7 @@ bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApk ALOGW("failed to find resources.arsc in %s\n", ap.path.string()); return false; } - tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */); + tables[i].add(ass); } return tables[0].createIdmap(tables[1], targetCrc, overlayCrc, @@ -617,7 +617,7 @@ const ResTable* AssetManager::getResTable(bool required) const // can quickly copy it out for others. ALOGV("Creating shared resources for %s", ap.path.string()); sharedRes = new ResTable(); - sharedRes->add(ass, i + 1, false, idmap); + sharedRes->add(ass, idmap, i + 1, false); #ifdef HAVE_ANDROID_OS const char* data = getenv("ANDROID_DATA"); LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set"); @@ -646,7 +646,7 @@ const ResTable* AssetManager::getResTable(bool required) const mResources->add(sharedRes); } else { ALOGV("Parsing resources for %s", ap.path.string()); - mResources->add(ass, i + 1, !shared, idmap); + mResources->add(ass, idmap, i + 1, !shared); } onlyEmptyResources = false; @@ -654,7 +654,7 @@ const ResTable* AssetManager::getResTable(bool required) const delete ass; } } else { - ALOGW("Installing empty resources in to table %p\n", mResources); + ALOGV("Installing empty resources in to table %p\n", mResources); mResources->addEmpty(i + 1); } @@ -736,7 +736,7 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList, if (oass != NULL) { Asset* oidmap = openIdmapLocked(oap); offset++; - sharedRes->add(oass, offset + 1, false, oidmap); + sharedRes->add(oass, oidmap, offset + 1, false); const_cast<AssetManager*>(this)->mAssetPaths.add(oap); const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap); } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index a4b78a6..2e3abb5 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -17,7 +17,9 @@ #define LOG_TAG "ResourceType" //#define LOG_NDEBUG 0 +#include <androidfw/ByteBucketArray.h> #include <androidfw/ResourceTypes.h> +#include <androidfw/TypeWrappers.h> #include <utils/Atomic.h> #include <utils/ByteOrder.h> #include <utils/Debug.h> @@ -30,6 +32,7 @@ #include <memory.h> #include <ctype.h> #include <stdint.h> +#include <stddef.h> #ifndef INT32_MAX #define INT32_MAX ((int32_t)(2147483647)) @@ -42,7 +45,7 @@ #define TABLE_SUPER_NOISY(x) //x #define LOAD_TABLE_NOISY(x) //x #define TABLE_THEME(x) //x -#define LIB_NOISY(x) x +#define LIB_NOISY(x) //x namespace android { @@ -63,9 +66,8 @@ namespace android { #endif #endif -#define IDMAP_MAGIC 0x706d6469 -// size measured in sizeof(uint32_t) -#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) +#define IDMAP_MAGIC 0x504D4449 +#define IDMAP_CURRENT_VERSION 0x00000001 #define APP_PACKAGE_ID 0x7f #define SYS_PACKAGE_ID 0x01 @@ -77,6 +79,11 @@ inline int isspace16(char16_t c) { return (c < 0x0080 && isspace(c)); } +template<typename T> +inline static T max(T a, T b) { + return a > b ? a : b; +} + // range checked; guaranteed to NUL-terminate within the stated number of available slots // NOTE: if this truncates the dst string due to running out of space, no attempt is // made to avoid splitting surrogate pairs. @@ -215,104 +222,179 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData)); } -static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) -{ - if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { - ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes); +static bool assertIdmapHeader(const void* idmap, size_t size) { + if (reinterpret_cast<uintptr_t>(idmap) & 0x03) { + ALOGE("idmap: header is not word aligned"); + return false; + } + + if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) { + ALOGW("idmap: header too small (%d bytes)", (uint32_t) size); return false; } - if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess - ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n", - *map, htodl(IDMAP_MAGIC)); + + const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap)); + if (magic != IDMAP_MAGIC) { + ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)", + magic, IDMAP_MAGIC); + return false; + } + + const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1)); + if (version != IDMAP_CURRENT_VERSION) { + // We are strict about versions because files with this format are + // auto-generated and don't need backwards compatibility. + ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)", + version, IDMAP_CURRENT_VERSION); return false; } return true; } -static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue) -{ - // see README for details on the format of map - if (!assertIdmapHeader(map, sizeBytes)) { - return UNKNOWN_ERROR; - } - map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment - // size of data block, in uint32_t - const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t); - const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id - const uint32_t entry = Res_GETENTRY(key); - const uint32_t typeCount = *map; +class IdmapEntries { +public: + IdmapEntries() : mData(NULL) {} - if (type > typeCount) { - ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount); - return UNKNOWN_ERROR; + bool hasEntries() const { + if (mData == NULL) { + return false; + } + + return (dtohs(*mData) > 0); } - if (typeCount > size) { - ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size); - return UNKNOWN_ERROR; + + size_t byteSize() const { + if (mData == NULL) { + return 0; + } + uint16_t entryCount = dtohs(mData[2]); + return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount)); } - const uint32_t typeOffset = map[type]; - if (typeOffset == 0) { - *outValue = 0; - return NO_ERROR; + + uint8_t targetTypeId() const { + if (mData == NULL) { + return 0; + } + return dtohs(mData[0]); } - if (typeOffset + 1 > size) { - ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", - typeOffset, (int)size); - return UNKNOWN_ERROR; + + uint8_t overlayTypeId() const { + if (mData == NULL) { + return 0; + } + return dtohs(mData[1]); } - const uint32_t entryCount = map[typeOffset]; - const uint32_t entryOffset = map[typeOffset + 1]; - if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) { - *outValue = 0; + + status_t setTo(const void* entryHeader, size_t size) { + if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) { + ALOGE("idmap: entry header is not word aligned"); + return UNKNOWN_ERROR; + } + + if (size < sizeof(uint16_t) * 4) { + ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size); + return UNKNOWN_ERROR; + } + + const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader); + const uint16_t targetTypeId = dtohs(header[0]); + const uint16_t overlayTypeId = dtohs(header[1]); + if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) { + ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId); + return UNKNOWN_ERROR; + } + + uint16_t entryCount = dtohs(header[2]); + if (size < sizeof(uint32_t) * (entryCount + 2)) { + ALOGE("idmap: too small (%u bytes) for the number of entries (%u)", + (uint32_t) size, (uint32_t) entryCount); + return UNKNOWN_ERROR; + } + mData = header; return NO_ERROR; } - const uint32_t index = typeOffset + 2 + entry - entryOffset; - if (index > size) { - ALOGW("Resource ID map: entry index=%u exceeds size of map=%d\n", index, (int)size); - *outValue = 0; + + status_t lookup(uint16_t entryId, uint16_t* outEntryId) const { + uint16_t entryCount = dtohs(mData[2]); + uint16_t offset = dtohs(mData[3]); + + if (entryId < offset) { + // The entry is not present in this idmap + return BAD_INDEX; + } + + entryId -= offset; + + if (entryId >= entryCount) { + // The entry is not present in this idmap + return BAD_INDEX; + } + + // It is safe to access the type here without checking the size because + // we have checked this when it was first loaded. + const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2; + uint32_t mappedEntry = dtohl(entries[entryId]); + if (mappedEntry == 0xffffffff) { + // This entry is not present in this idmap + return BAD_INDEX; + } + *outEntryId = static_cast<uint16_t>(mappedEntry); return NO_ERROR; } - *outValue = map[index]; - return NO_ERROR; -} +private: + const uint16_t* mData; +}; -static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId) -{ - if (!assertIdmapHeader(map, mapSize)) { +status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) { + if (!assertIdmapHeader(idmap, size)) { return UNKNOWN_ERROR; } - if (mapSize <= IDMAP_HEADER_SIZE + 1) { - ALOGW("corrupt idmap: map size %d too short\n", (int)mapSize); + + size -= ResTable::IDMAP_HEADER_SIZE_BYTES; + if (size < sizeof(uint16_t) * 2) { + ALOGE("idmap: too small to contain any mapping"); return UNKNOWN_ERROR; } - uint32_t typeCount = *(map + IDMAP_HEADER_SIZE); - if (typeCount == 0) { - ALOGW("corrupt idmap: no types\n"); + + const uint16_t* data = reinterpret_cast<const uint16_t*>( + reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES); + + uint16_t targetPackageId = dtohs(*(data++)); + if (targetPackageId == 0 || targetPackageId > 255) { + ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId); return UNKNOWN_ERROR; } - if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) { - ALOGW("corrupt idmap: number of types %u extends past idmap size %d\n", typeCount, (int)mapSize); + + uint16_t mapCount = dtohs(*(data++)); + if (mapCount == 0) { + ALOGE("idmap: no mappings"); return UNKNOWN_ERROR; } - const uint32_t* p = map + IDMAP_HEADER_SIZE + 1; - // find first defined type - while (*p == 0) { - ++p; - if (--typeCount == 0) { - ALOGW("corrupt idmap: types declared, none found\n"); - return UNKNOWN_ERROR; - } + + if (mapCount > 255) { + ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount); } - // determine package id from first entry of first type - const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2; - if (offset > mapSize) { - ALOGW("corrupt idmap: entry offset %u points outside map size %d\n", offset, (int)mapSize); - return UNKNOWN_ERROR; + while (size > sizeof(uint16_t) * 4) { + IdmapEntries entries; + status_t err = entries.setTo(data, size); + if (err != NO_ERROR) { + return err; + } + + ssize_t index = outMap->add(entries.overlayTypeId(), entries); + if (index < 0) { + return NO_MEMORY; + } + + data += entries.byteSize() / sizeof(uint16_t); + size -= entries.byteSize(); } - *outId = (map[offset] >> 24) & 0x000000ff; + if (outPackageId != NULL) { + *outPackageId = static_cast<uint8_t>(targetPackageId); + } return NO_ERROR; } @@ -2726,7 +2808,7 @@ struct ResTable::Header free(resourceIDMap); } - ResTable* const owner; + const ResTable* const owner; void* ownedData; const ResTable_header* header; size_t size; @@ -2739,6 +2821,17 @@ struct ResTable::Header size_t resourceIDMapSize; }; +struct ResTable::Entry { + ResTable_config config; + const ResTable_entry* entry; + const ResTable_type* type; + uint32_t specFlags; + const Package* package; + + StringPoolRef typeStr; + StringPoolRef keyStr; +}; + struct ResTable::Type { Type(const Header* _header, const Package* _package, size_t count) @@ -2749,33 +2842,29 @@ struct ResTable::Type const size_t entryCount; const ResTable_typeSpec* typeSpec; const uint32_t* typeSpecFlags; + IdmapEntries idmapEntries; Vector<const ResTable_type*> configs; }; struct ResTable::Package { Package(ResTable* _owner, const Header* _header, const ResTable_package* _package) - : owner(_owner), header(_header), package(_package) { } - ~Package() - { - size_t i = types.size(); - while (i > 0) { - i--; - delete types[i]; + : owner(_owner), header(_header), package(_package), typeIdOffset(0) { + if (dtohs(package->header.headerSize) == sizeof(package)) { + // The package structure is the same size as the definition. + // This means it contains the typeIdOffset field. + typeIdOffset = package->typeIdOffset; } } - ResTable* const owner; + const ResTable* const owner; const Header* const header; const ResTable_package* const package; - Vector<Type*> types; ResStringPool typeStrings; ResStringPool keyStrings; - const Type* getType(size_t idx) const { - return idx < types.size() ? types[idx] : NULL; - } + size_t typeIdOffset; }; // A group of objects describing a particular resource package. @@ -2787,13 +2876,24 @@ struct ResTable::PackageGroup : owner(_owner) , name(_name) , id(_id) - , typeCount(0) + , largestTypeId(0) , bags(NULL) , dynamicRefTable(static_cast<uint8_t>(_id)) { } ~PackageGroup() { clearBagCache(); + const size_t numTypes = types.size(); + for (size_t i = 0; i < numTypes; i++) { + const TypeList& typeList = types[i]; + const size_t numInnerTypes = typeList.size(); + for (size_t j = 0; j < numInnerTypes; j++) { + if (typeList[j]->package->owner == owner) { + delete typeList[j]; + } + } + } + const size_t N = packages.size(); for (size_t i=0; i<N; i++) { Package* pkg = packages[i]; @@ -2806,17 +2906,15 @@ struct ResTable::PackageGroup void clearBagCache() { if (bags) { TABLE_NOISY(printf("bags=%p\n", bags)); - Package* pkg = packages[0]; - TABLE_NOISY(printf("typeCount=%x\n", typeCount)); - for (size_t i=0; i<typeCount; i++) { + for (size_t i = 0; i < bags->size(); i++) { TABLE_NOISY(printf("type=%d\n", i)); - const Type* type = pkg->getType(i); - if (type != NULL) { - bag_set** typeBags = bags[i]; + const TypeList& typeList = types[i]; + if (typeList.isEmpty()) { + bag_set** typeBags = bags->get(i); TABLE_NOISY(printf("typeBags=%p\n", typeBags)); if (typeBags) { - TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); - const size_t N = type->entryCount; + const size_t N = typeList[0]->entryCount; + TABLE_NOISY(printf("type->entryCount=%x\n", N)); for (size_t j=0; j<N; j++) { if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) free(typeBags[j]); @@ -2825,25 +2923,38 @@ struct ResTable::PackageGroup } } } - free(bags); + delete bags; bags = NULL; } } - ResTable* const owner; + ssize_t findType16(const char16_t* type, size_t len) const { + const size_t N = packages.size(); + for (size_t i = 0; i < N; i++) { + ssize_t index = packages[i]->typeStrings.indexOfString(type, len); + if (index >= 0) { + return index + packages[i]->typeIdOffset; + } + } + return -1; + } + + const ResTable* const owner; String16 const name; uint32_t const id; + + // This is mainly used to keep track of the loaded packages + // and to clean them up properly. Accessing resources happens from + // the 'types' array. Vector<Package*> packages; - // This is for finding typeStrings and other common package stuff. - Package* basePackage; + ByteBucketArray<TypeList> types; - // For quick access. - size_t typeCount; + uint8_t largestTypeId; // Computed attribute bags, first indexed by the type and second // by the entry in that type. - bag_set*** bags; + ByteBucketArray<bag_set**>* bags; // The table mapping dynamic references to resolved references for // this package group. @@ -2879,7 +2990,7 @@ ResTable::Theme::~Theme() void ResTable::Theme::free_package(package_info* pi) { - for (size_t j=0; j<pi->numTypes; j++) { + for (size_t j = 0; j <= Res_MAXTYPE; j++) { theme_entry* te = pi->types[j].entries; if (te != NULL) { free(te); @@ -2890,10 +3001,8 @@ void ResTable::Theme::free_package(package_info* pi) ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) { - package_info* newpi = (package_info*)malloc( - sizeof(package_info) + (pi->numTypes*sizeof(type_info))); - newpi->numTypes = pi->numTypes; - for (size_t j=0; j<newpi->numTypes; j++) { + package_info* newpi = (package_info*)malloc(sizeof(package_info)); + for (size_t j = 0; j <= Res_MAXTYPE; j++) { size_t cnt = pi->types[j].numEntries; newpi->types[j].numEntries = cnt; theme_entry* te = pi->types[j].entries; @@ -2946,17 +3055,14 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) curPI = mPackages[pidx]; if (curPI == NULL) { PackageGroup* const grp = mTable.mPackageGroups[pidx]; - int cnt = grp->typeCount; - curPI = (package_info*)malloc( - sizeof(package_info) + (cnt*sizeof(type_info))); - curPI->numTypes = cnt; - memset(curPI->types, 0, cnt*sizeof(type_info)); + curPI = (package_info*)malloc(sizeof(package_info)); + memset(curPI, 0, sizeof(*curPI)); mPackages[pidx] = curPI; } curType = 0xffffffff; } if (curType != t) { - if (t >= curPI->numTypes) { + if (t > Res_MAXTYPE) { ALOGE("Style contains key with bad type: 0x%08x\n", attrRes); bag++; continue; @@ -2965,8 +3071,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) curEntries = curPI->types[t].entries; if (curEntries == NULL) { PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; - const Type* type = grp->packages[0]->getType(t); - int cnt = type != NULL ? type->entryCount : 0; + const TypeList& typeList = grp->types[t]; + int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount; curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); curPI->types[t].numEntries = cnt; @@ -2981,8 +3087,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) } theme_entry* curEntry = curEntries + e; TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", - attrRes, bag->map.value.dataType, bag->map.value.data, - curEntry->value.dataType)); + attrRes, bag->map.value.dataType, bag->map.value.data, + curEntry->value.dataType)); if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { curEntry->stringBlock = bag->stringBlock; curEntry->typeSpecFlags |= bagTypeSpecFlags; @@ -3057,8 +3163,8 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, const package_info* const pi = mPackages[p]; TABLE_THEME(ALOGI("Found package: %p", pi)); if (pi != NULL) { - TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, pi->numTypes)); - if (t < pi->numTypes) { + TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1)); + if (t <= Res_MAXTYPE) { const type_info& ti = pi->types[t]; TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries)); if (e < ti.numEntries) { @@ -3120,14 +3226,13 @@ void ResTable::Theme::dumpToLog() const package_info* pi = mPackages[i]; if (pi == NULL) continue; - ALOGI(" Package #0x%02x:\n", (int)(i+1)); - for (size_t j=0; j<pi->numTypes; j++) { + ALOGI(" Package #0x%02x:\n", (int)(i + 1)); + for (size_t j = 0; j <= Res_MAXTYPE; j++) { type_info& ti = pi->types[j]; if (ti.numEntries == 0) continue; - - ALOGI(" Type #0x%02x:\n", (int)(j+1)); - for (size_t k=0; k<ti.numEntries; k++) { - theme_entry& te = ti.entries[k]; + ALOGI(" Type #0x%02x:\n", (int)(j + 1)); + for (size_t k = 0; k < ti.numEntries; k++) { + const theme_entry& te = ti.entries[k]; if (te.value.dataType == Res_value::TYPE_NULL) continue; ALOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n", (int)Res_MAKEID(i, j, k), @@ -3150,7 +3255,7 @@ ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool cop { memset(&mParams, 0, sizeof(mParams)); memset(mPackageMap, 0, sizeof(mPackageMap)); - addInternal(data, size, cookie, copyData, NULL /* idMap */); + addInternal(data, size, NULL, 0, cookie, copyData); LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table"); //ALOGI("Creating ResTable %p\n", this); } @@ -3166,21 +3271,45 @@ inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1; } -status_t ResTable::add(const void* data, size_t size) { - return addInternal(data, size, 0 /* cookie */, - false /* copyData */, NULL /* idMap */); +status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) { + return addInternal(data, size, NULL, 0, cookie, copyData); } -status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const void* idmap) -{ +status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, + const int32_t cookie, bool copyData) { + return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData); +} + +status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) { const void* data = asset->getBuffer(true); if (data == NULL) { ALOGW("Unable to get buffer of resource asset file"); return UNKNOWN_ERROR; } - size_t size = (size_t)asset->getLength(); - return addInternal(data, size, cookie, copyData, - reinterpret_cast<const Asset*>(idmap)); + + return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData); +} + +status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) { + const void* data = asset->getBuffer(true); + if (data == NULL) { + ALOGW("Unable to get buffer of resource asset file"); + return UNKNOWN_ERROR; + } + + size_t idmapSize = 0; + const void* idmapData = NULL; + if (idmapAsset != NULL) { + idmapData = idmapAsset->getBuffer(true); + if (idmapData == NULL) { + ALOGW("Unable to get buffer of idmap asset file"); + return UNKNOWN_ERROR; + } + idmapSize = static_cast<size_t>(idmapAsset->getLength()); + } + + return addInternal(data, static_cast<size_t>(asset->getLength()), + idmapData, idmapSize, cookie, copyData); } status_t ResTable::add(ResTable* src) @@ -3197,8 +3326,16 @@ status_t ResTable::add(ResTable* src) for (size_t j=0; j<srcPg->packages.size(); j++) { pg->packages.add(srcPg->packages[j]); } - pg->basePackage = srcPg->basePackage; - pg->typeCount = srcPg->typeCount; + + for (size_t j = 0; j < srcPg->types.size(); j++) { + if (srcPg->types[j].isEmpty()) { + continue; + } + + TypeList& typeList = pg->types.editItemAt(j); + typeList.appendVector(srcPg->types[j]); + } + pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId); mPackageGroups.add(pg); } @@ -3224,38 +3361,39 @@ status_t ResTable::addEmpty(const int32_t cookie) { return (mError=NO_ERROR); } -status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie, - bool copyData, const Asset* idmap) +status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize, + const int32_t cookie, bool copyData) { - if (!data) return NO_ERROR; + if (!data) { + return NO_ERROR; + } + Header* header = new Header(this); header->index = mHeaders.size(); header->cookie = cookie; - if (idmap != NULL) { - const size_t idmap_size = idmap->getLength(); - const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true); - header->resourceIDMap = (uint32_t*)malloc(idmap_size); + if (idmapData != NULL) { + header->resourceIDMap = (uint32_t*) malloc(idmapDataSize); if (header->resourceIDMap == NULL) { delete header; return (mError = NO_MEMORY); } - memcpy((void*)header->resourceIDMap, idmap_data, idmap_size); - header->resourceIDMapSize = idmap_size; + memcpy(header->resourceIDMap, idmapData, idmapDataSize); + header->resourceIDMapSize = idmapDataSize; } mHeaders.add(header); const bool notDeviceEndian = htods(0xf0) != 0xf0; LOAD_TABLE_NOISY( - ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d " - "idmap=%p\n", data, size, cookie, asset, copyData, idmap)); + ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d " + "idmap=%p\n", data, dataSize, cookie, copyData, idmap)); if (copyData || notDeviceEndian) { - header->ownedData = malloc(size); + header->ownedData = malloc(dataSize); if (header->ownedData == NULL) { return (mError=NO_MEMORY); } - memcpy(header->ownedData, data, size); + memcpy(header->ownedData, data, dataSize); data = header->ownedData; } @@ -3265,10 +3403,10 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook // dtohl(header->header->header.size), header->header->header.size); LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header)); if (dtohs(header->header->header.headerSize) > header->size - || header->size > size) { + || header->size > dataSize) { ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", (int)dtohs(header->header->header.headerSize), - (int)header->size, (int)size); + (int)header->size, (int)dataSize); return (mError=BAD_TYPE); } if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { @@ -3313,16 +3451,8 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook dtohl(header->header->packageCount)); return (mError=BAD_TYPE); } - uint32_t idmap_id = 0; - if (idmap != NULL) { - uint32_t tmp; - if (getIdmapPackageId(header->resourceIDMap, - header->resourceIDMapSize, - &tmp) == NO_ERROR) { - idmap_id = tmp; - } - } - if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) { + + if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { return mError; } curPackage++; @@ -3405,46 +3535,38 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou ALOGW("Bad identifier when getting name for resource number 0x%08x", resID); return false; } - if (grp->packages.size() > 0) { - const Package* const package = grp->packages[0]; - const ResTable_type* type; - const ResTable_entry* entry; - ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); - if (offset <= 0) { - return false; - } + Entry entry; + status_t err = getEntry(grp, t, e, NULL, &entry); + if (err != NO_ERROR) { + return false; + } - outName->package = grp->name.string(); - outName->packageLen = grp->name.size(); - if (allowUtf8) { - outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen); - outName->name8 = grp->basePackage->keyStrings.string8At( - dtohl(entry->key.index), &outName->nameLen); - } else { - outName->type8 = NULL; - outName->name8 = NULL; - } - if (outName->type8 == NULL) { - outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); - // If we have a bad index for some reason, we should abort. - if (outName->type == NULL) { - return false; - } + outName->package = grp->name.string(); + outName->packageLen = grp->name.size(); + if (allowUtf8) { + outName->type8 = entry.typeStr.string8(&outName->typeLen); + outName->name8 = entry.keyStr.string8(&outName->nameLen); + } else { + outName->type8 = NULL; + outName->name8 = NULL; + } + if (outName->type8 == NULL) { + outName->type = entry.typeStr.string16(&outName->typeLen); + // If we have a bad index for some reason, we should abort. + if (outName->type == NULL) { + return false; } - if (outName->name8 == NULL) { - outName->name = grp->basePackage->keyStrings.stringAt( - dtohl(entry->key.index), &outName->nameLen); - // If we have a bad index for some reason, we should abort. - if (outName->name == NULL) { - return false; - } + } + if (outName->name8 == NULL) { + outName->name = entry.keyStr.string16(&outName->nameLen); + // If we have a bad index for some reason, we should abort. + if (outName->name == NULL) { + return false; } - - return true; } - return false; + return true; } ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, @@ -3471,15 +3593,6 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag return BAD_INDEX; } - const Res_value* bestValue = NULL; - const Package* bestPackage = NULL; - ResTable_config bestItem; - memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up - - if (outSpecFlags != NULL) *outSpecFlags = 0; - - // Look through all resource packages, starting with the most - // recently added. const PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { ALOGW("Bad identifier when getting value for resource number 0x%08x", resID); @@ -3487,142 +3600,62 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag } // Allow overriding density - const ResTable_config* desiredConfig = &mParams; - ResTable_config* overrideConfig = NULL; + ResTable_config desiredConfig = mParams; if (density > 0) { - overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); - if (overrideConfig == NULL) { - ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); - return BAD_INDEX; - } - memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); - overrideConfig->density = density; - desiredConfig = overrideConfig; - } - - ssize_t rc = BAD_VALUE; - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", - resID, T, E, ip, (int)offset); - rc = offset; - goto out; - } - continue; - } + desiredConfig.density = density; + } - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { - if (!mayBeBag) { - ALOGW("Requesting resource 0x%x failed because it is complex\n", - resID); - } - continue; - } + Entry entry; + status_t err = getEntry(grp, t, e, &desiredConfig, &entry); + if (err != NO_ERROR) { + ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n", + resID, t, e, err); + return err; + } - if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { - ALOGW("ResTable_item at %d is beyond type chunk data %d", - (int)offset, dtohl(type->header.size)); - rc = BAD_TYPE; - goto out; + if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) { + if (!mayBeBag) { + ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID); } + return BAD_VALUE; + } - const Res_value* item = - (const Res_value*)(((const uint8_t*)type) + offset); - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); + const Res_value* value = reinterpret_cast<const Res_value*>( + reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size); - if (outSpecFlags != NULL) { - if (typeClass->typeSpecFlags != NULL) { - *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - *outSpecFlags = -1; - } - } + outValue->size = dtohs(value->size); + outValue->res0 = value->res0; + outValue->dataType = value->dataType; + outValue->data = dtohl(value->data); - if (bestPackage != NULL && - (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) { - // Discard thisConfig not only if bestItem is more specific, but also if the two configs - // are identical (diff == 0), or overlay packages will not take effect. - continue; - } - - bestItem = thisConfig; - bestValue = item; - bestPackage = package; + // The reference may be pointing to a resource in a shared library. These + // references have build-time generated package IDs. These ids may not match + // the actual package IDs of the corresponding packages in this ResTable. + // We need to fix the package ID based on a mapping. + if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) { + ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data); + return BAD_VALUE; } - TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); - - if (bestValue) { - outValue->size = dtohs(bestValue->size); - outValue->res0 = bestValue->res0; - outValue->dataType = bestValue->dataType; - outValue->data = dtohl(bestValue->data); - - // The reference may be pointing to a resource in a shared library. These - // references have build-time generated package IDs. These ids may not match - // the actual package IDs of the corresponding packages in this ResTable. - // We need to fix the package ID based on a mapping. - status_t err = grp->dynamicRefTable.lookupResourceValue(outValue); - if (err != NO_ERROR) { - ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data); - rc = BAD_VALUE; - goto out; - } + TABLE_NOISY(size_t len; + printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", + entry.package->header->index, + outValue->dataType, + outValue->dataType == Res_value::TYPE_STRING + ? String8(entry.package->header->values.stringAt( + outValue->data, &len)).string() + : "", + outValue->data)); - if (outConfig != NULL) { - *outConfig = bestItem; - } - TABLE_NOISY(size_t len; - printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", - bestPackage->header->index, - outValue->dataType, - outValue->dataType == bestValue->TYPE_STRING - ? String8(bestPackage->header->values.stringAt( - outValue->data, &len)).string() - : "", - outValue->data)); - rc = bestPackage->header->index; - goto out; + if (outSpecFlags != NULL) { + *outSpecFlags = entry.specFlags; } -out: - if (overrideConfig != NULL) { - free(overrideConfig); + if (outConfig != NULL) { + *outConfig = entry.config; } - return rc; + return entry.package->header->index; } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, @@ -3721,29 +3754,25 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID); - return false; + return BAD_INDEX; } - if (t >= (int)grp->typeCount) { - ALOGW("Type identifier 0x%x is larger than type count 0x%x", - t+1, (int)grp->typeCount); + const TypeList& typeConfigs = grp->types[t]; + if (typeConfigs.isEmpty()) { + ALOGW("Type identifier 0x%x does not exist.", t+1); return BAD_INDEX; } - const Package* const basePackage = grp->packages[0]; - - const Type* const typeConfigs = basePackage->getType(t); - - const size_t NENTRY = typeConfigs->entryCount; + const size_t NENTRY = typeConfigs[0]->entryCount; if (e >= (int)NENTRY) { ALOGW("Entry identifier 0x%x is larger than entry count 0x%x", - e, (int)typeConfigs->entryCount); + e, (int)typeConfigs[0]->entryCount); return BAD_INDEX; } // First see if we've already computed this bag... if (grp->bags) { - bag_set** typeSet = grp->bags[t]; + bag_set** typeSet = grp->bags->get(t); if (typeSet) { bag_set* set = typeSet[e]; if (set) { @@ -3764,229 +3793,174 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, // Bag not found, we need to compute it! if (!grp->bags) { - grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*)); + grp->bags = new ByteBucketArray<bag_set**>(); if (!grp->bags) return NO_MEMORY; } - bag_set** typeSet = grp->bags[t]; + bag_set** typeSet = grp->bags->get(t); if (!typeSet) { typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*)); if (!typeSet) return NO_MEMORY; - grp->bags[t] = typeSet; + grp->bags->set(t, typeSet); } // Mark that we are currently working on this one. typeSet[e] = (bag_set*)0xFFFFFFFF; - // This is what we are building. - bag_set* set = NULL; - TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID)); - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); + // Now collect all bag attributes + Entry entry; + status_t err = getEntry(grp, t, e, &mParams, &entry); + if (err != NO_ERROR) { + return err; + } - // Now collect all bag attributes from all packages. - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } + const uint16_t entrySize = dtohs(entry.entry->size); + const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0; + const uint32_t count = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0; - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); - ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); - ALOGV("Resulting offset=%d\n", (int)offset); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - if (set) free(set); - return offset; - } - continue; - } + size_t N = count; - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { - ALOGW("Skipping entry 0x%x in package table %zu because it is not complex!\n", - resID, ip); - continue; + TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n", + entrySize, parent, count)); + + // If this map inherits from another, we need to start + // with its parent's values. Otherwise start out empty. + TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", + entrySize, parent)); + + // This is what we are building. + bag_set* set = NULL; + + if (parent) { + uint32_t resolvedParent = parent; + + // Bags encode a parent reference without using the standard + // Res_value structure. That means we must always try to + // resolve a parent reference in case it is actually a + // TYPE_DYNAMIC_REFERENCE. + status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent); + if (err != NO_ERROR) { + ALOGE("Failed resolving bag parent id 0x%08x", parent); + return UNKNOWN_ERROR; } - if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) { - continue; + const bag_entry* parentBag; + uint32_t parentTypeSpecFlags = 0; + const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags); + const size_t NT = ((NP >= 0) ? NP : 0) + N; + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); + if (set == NULL) { + return NO_MEMORY; } - bestConfig = type->config; - if (set) { - free(set); - set = NULL; + if (NP > 0) { + memcpy(set+1, parentBag, NP*sizeof(bag_entry)); + set->numAttrs = NP; + TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP)); + } else { + TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n")); + set->numAttrs = 0; } + set->availAttrs = NT; + set->typeSpecFlags = parentTypeSpecFlags; + } else { + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); + if (set == NULL) { + return NO_MEMORY; + } + set->numAttrs = 0; + set->availAttrs = N; + set->typeSpecFlags = 0; + } - const uint16_t entrySize = dtohs(entry->size); - const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; - const uint32_t count = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; - - size_t N = count; + set->typeSpecFlags |= entry.specFlags; - TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n", - entrySize, parent, count)); + // Now merge in the new attributes... + size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type)) + + dtohs(entry.entry->size); + const ResTable_map* map; + bag_entry* entries = (bag_entry*)(set+1); + size_t curEntry = 0; + uint32_t pos = 0; + TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + while (pos < count) { + TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - // If this map inherits from another, we need to start - // with its parent's values. Otherwise start out empty. - TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", - entrySize, parent)); - if (parent) { - uint32_t resolvedParent = parent; + if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) { + ALOGW("ResTable_map at %d is beyond type chunk data %d", + (int)curOff, dtohl(entry.type->header.size)); + return BAD_TYPE; + } + map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff); + N++; - // Bags encode a parent reference without using the standard - // Res_value structure. That means we must always try to - // resolve a parent reference in case it is actually a - // TYPE_DYNAMIC_REFERENCE. - status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent); - if (err != NO_ERROR) { - ALOGE("Failed resolving bag parent id 0x%08x", parent); - return UNKNOWN_ERROR; - } + const uint32_t newName = htodl(map->name.ident); + bool isInside; + uint32_t oldName = 0; + while ((isInside=(curEntry < set->numAttrs)) + && (oldName=entries[curEntry].map.name.ident) < newName) { + TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", + curEntry, entries[curEntry].map.name.ident)); + curEntry++; + } - const bag_entry* parentBag; - uint32_t parentTypeSpecFlags = 0; - const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags); - const size_t NT = ((NP >= 0) ? NP : 0) + N; - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); - if (set == NULL) { - return NO_MEMORY; - } - if (NP > 0) { - memcpy(set+1, parentBag, NP*sizeof(bag_entry)); - set->numAttrs = NP; - TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP)); - } else { - TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n")); - set->numAttrs = 0; - } - set->availAttrs = NT; - set->typeSpecFlags = parentTypeSpecFlags; + if ((!isInside) || oldName != newName) { + // This is a new attribute... figure out what to do with it. + if (set->numAttrs >= set->availAttrs) { + // Need to alloc more memory... + const size_t newAvail = set->availAttrs+N; + set = (bag_set*)realloc(set, + sizeof(bag_set) + + sizeof(bag_entry)*newAvail); + if (set == NULL) { + return NO_MEMORY; + } + set->availAttrs = newAvail; + entries = (bag_entry*)(set+1); + TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + } + if (isInside) { + // Going in the middle, need to make space. + memmove(entries+curEntry+1, entries+curEntry, + sizeof(bag_entry)*(set->numAttrs-curEntry)); + set->numAttrs++; + } + TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", + curEntry, newName)); } else { - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); - if (set == NULL) { - return NO_MEMORY; - } - set->numAttrs = 0; - set->availAttrs = N; - set->typeSpecFlags = 0; + TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", + curEntry, oldName)); } - if (typeClass->typeSpecFlags != NULL) { - set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - set->typeSpecFlags = -1; - } - - // Now merge in the new attributes... - ssize_t curOff = offset; - const ResTable_map* map; - bag_entry* entries = (bag_entry*)(set+1); - size_t curEntry = 0; - uint32_t pos = 0; - TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - while (pos < count) { - TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - - if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { - ALOGW("ResTable_map at %d is beyond type chunk data %d", - (int)curOff, dtohl(type->header.size)); - return BAD_TYPE; - } - map = (const ResTable_map*)(((const uint8_t*)type) + curOff); - N++; - - const uint32_t newName = htodl(map->name.ident); - bool isInside; - uint32_t oldName = 0; - while ((isInside=(curEntry < set->numAttrs)) - && (oldName=entries[curEntry].map.name.ident) < newName) { - TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", - curEntry, entries[curEntry].map.name.ident)); - curEntry++; - } - - if ((!isInside) || oldName != newName) { - // This is a new attribute... figure out what to do with it. - if (set->numAttrs >= set->availAttrs) { - // Need to alloc more memory... - const size_t newAvail = set->availAttrs+N; - set = (bag_set*)realloc(set, - sizeof(bag_set) - + sizeof(bag_entry)*newAvail); - if (set == NULL) { - return NO_MEMORY; - } - set->availAttrs = newAvail; - entries = (bag_entry*)(set+1); - TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - } - if (isInside) { - // Going in the middle, need to make space. - memmove(entries+curEntry+1, entries+curEntry, - sizeof(bag_entry)*(set->numAttrs-curEntry)); - set->numAttrs++; - } - TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", - curEntry, newName)); - } else { - TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", - curEntry, oldName)); - } + bag_entry* cur = entries+curEntry; - bag_entry* cur = entries+curEntry; + cur->stringBlock = entry.package->header->index; + cur->map.name.ident = newName; + cur->map.value.copyFrom_dtoh(map->value); + status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value); + if (err != NO_ERROR) { + ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data); + return UNKNOWN_ERROR; + } - cur->stringBlock = package->header->index; - cur->map.name.ident = newName; - cur->map.value.copyFrom_dtoh(map->value); - status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value); - if (err != NO_ERROR) { - ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data); - return UNKNOWN_ERROR; - } + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data)); - TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", - curEntry, cur, cur->stringBlock, cur->map.name.ident, - cur->map.value.dataType, cur->map.value.data)); + // On to the next! + curEntry++; + pos++; + const size_t size = dtohs(map->value.size); + curOff += size + sizeof(*map)-sizeof(map->value); + }; - // On to the next! - curEntry++; - pos++; - const size_t size = dtohs(map->value.size); - curOff += size + sizeof(*map)-sizeof(map->value); - }; - if (curEntry > set->numAttrs) { - set->numAttrs = curEntry; - } + if (curEntry > set->numAttrs) { + set->numAttrs = curEntry; } // And this is it... @@ -4154,80 +4128,63 @@ nope: continue; } - const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen); + const ssize_t ti = group->findType16(type, typeLen); if (ti < 0) { TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); continue; } - const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen); - if (ei < 0) { - TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); - continue; - } - - TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); - - const Type* const typeConfigs = group->packages[0]->getType(ti); - if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { - TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", + const TypeList& typeList = group->types[ti]; + if (typeList.isEmpty()) { + TABLE_NOISY(printf("Expected type structure not found in package %s for index %d\n", String8(group->name).string(), ti)); + continue; } - size_t NTC = typeConfigs->configs.size(); - for (size_t tci=0; tci<NTC; tci++) { - const ResTable_type* const ty = typeConfigs->configs[tci]; - const uint32_t typeOffset = dtohl(ty->entriesStart); - - const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); - - const size_t NE = dtohl(ty->entryCount); - for (size_t i=0; i<NE; i++) { - uint32_t offset = dtohl(eindex[i]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - offset += typeOffset; + const size_t typeCount = typeList.size(); + for (size_t i = 0; i < typeCount; i++) { + const Type* t = typeList[i]; + const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen); + if (ei < 0) { + continue; + } - if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) { - ALOGW("ResTable_entry at %d is beyond type chunk data %d", - offset, dtohl(ty->header.size)); - return 0; - } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", - (int)offset, (int)group->id, (int)ti+1, (int)i, - String8(package, packageLen).string(), - String8(type, typeLen).string(), - String8(name, nameLen).string()); - return 0; - } + const size_t configCount = t->configs.size(); + for (size_t j = 0; j < configCount; j++) { + const TypeVariant tv(t->configs[j]); + for (TypeVariant::iterator iter = tv.beginEntries(); + iter != tv.endEntries(); + iter++) { + const ResTable_entry* entry = *iter; + if (entry == NULL) { + continue; + } - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)ty) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - ALOGW("ResTable_entry size %d is too small", dtohs(entry->size)); - return BAD_TYPE; - } + if (dtohl(entry->key.index) == (size_t) ei) { + uint32_t resId = Res_MAKEID(group->id - 1, ti, iter.index()); + if (outTypeSpecFlags) { + Entry result; + if (getEntry(group, ti, iter.index(), NULL, &result) != NO_ERROR) { + ALOGW("Failed to find spec flags for %s:%s/%s (0x%08x)", + String8(group->name).string(), + String8(String16(type, typeLen)).string(), + String8(String16(name, nameLen)).string(), + resId); + return 0; + } + *outTypeSpecFlags = result.specFlags; - TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", - i, ei, dtohl(entry->key.index))); - if (dtohl(entry->key.index) == (size_t)ei) { - if (outTypeSpecFlags) { - *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; - if (fakePublic) { - *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; + if (fakePublic) { + *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; + } } + return resId; } - return Res_MAKEID(group->id-1, ti, i); } } } + break; } - return 0; } @@ -5260,6 +5217,18 @@ uint32_t ResTable::getBasePackageId(size_t idx) const return mPackageGroups[idx]->id; } +uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + const PackageGroup* const group = mPackageGroups[idx]; + return group->largestTypeId; +} + size_t ResTable::getTableCount() const { return mHeaders.size(); @@ -5292,32 +5261,31 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con void ResTable::getConfigurations(Vector<ResTable_config>* configs) const { - const size_t I = mPackageGroups.size(); - for (size_t i=0; i<I; i++) { + const size_t packageCount = mPackageGroups.size(); + for (size_t i = 0; i < packageCount; i++) { const PackageGroup* packageGroup = mPackageGroups[i]; - const size_t J = packageGroup->packages.size(); - for (size_t j=0; j<J; j++) { - const Package* package = packageGroup->packages[j]; - const size_t K = package->types.size(); - for (size_t k=0; k<K; k++) { - const Type* type = package->types[k]; - if (type == NULL) continue; - const size_t L = type->configs.size(); - for (size_t l=0; l<L; l++) { - const ResTable_type* config = type->configs[l]; + const size_t typeCount = packageGroup->types.size(); + for (size_t j = 0; j < typeCount; j++) { + const TypeList& typeList = packageGroup->types[j]; + const size_t numTypes = typeList.size(); + for (size_t k = 0; k < numTypes; k++) { + const Type* type = typeList[k]; + const size_t numConfigs = type->configs.size(); + for (size_t m = 0; m < numConfigs; m++) { + const ResTable_type* config = type->configs[m]; ResTable_config cfg; memset(&cfg, 0, sizeof(ResTable_config)); cfg.copyFromDtoH(config->config); // only insert unique - const size_t M = configs->size(); - size_t m; - for (m=0; m<M; m++) { - if (0 == (*configs)[m].compare(cfg)) { + const size_t N = configs->size(); + size_t n; + for (n = 0; n < N; n++) { + if (0 == (*configs)[n].compare(cfg)) { break; } } // if we didn't find it - if (m == M) { + if (n == N) { configs->add(cfg); } } @@ -5350,122 +5318,180 @@ void ResTable::getLocales(Vector<String8>* locales) const } } -ssize_t ResTable::getEntry( - const Package* package, int typeIndex, int entryIndex, - const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const -{ - ALOGV("Getting entry from package %p\n", package); - const ResTable_package* const pkg = package->package; +StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index) + : mPool(pool), mIndex(index) {} - const Type* allTypes = package->getType(typeIndex); - ALOGV("allTypes=%p\n", allTypes); - if (allTypes == NULL) { - ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); - return 0; +StringPoolRef::StringPoolRef() + : mPool(NULL), mIndex(0) {} + +const char* StringPoolRef::string8(size_t* outLen) const { + if (mPool != NULL) { + return mPool->string8At(mIndex, outLen); } + if (outLen != NULL) { + *outLen = 0; + } + return NULL; +} - if ((size_t)entryIndex >= allTypes->entryCount) { - ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", - entryIndex, (int)allTypes->entryCount); +const char16_t* StringPoolRef::string16(size_t* outLen) const { + if (mPool != NULL) { + return mPool->stringAt(mIndex, outLen); + } + if (outLen != NULL) { + *outLen = 0; + } + return NULL; +} + +status_t ResTable::getEntry( + const PackageGroup* packageGroup, int typeIndex, int entryIndex, + const ResTable_config* config, + Entry* outEntry) const +{ + const TypeList& typeList = packageGroup->types[typeIndex]; + if (typeList.isEmpty()) { + ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); return BAD_TYPE; } - const ResTable_type* type = NULL; - uint32_t offset = ResTable_type::NO_ENTRY; + const ResTable_type* bestType = NULL; + uint32_t bestOffset = ResTable_type::NO_ENTRY; + const Package* bestPackage = NULL; + uint32_t specFlags = 0; + uint8_t actualTypeIndex = typeIndex; ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up - - const size_t NT = allTypes->configs.size(); - for (size_t i=0; i<NT; i++) { - const ResTable_type* const thisType = allTypes->configs[i]; - if (thisType == NULL) continue; - - ResTable_config thisConfig; - thisConfig.copyFromDtoH(thisType->config); + memset(&bestConfig, 0, sizeof(bestConfig)); - TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n", - entryIndex, typeIndex+1, dtohl(thisType->config.size), - thisConfig.toString().string())); + // Iterate over the Types of each package. + const size_t typeCount = typeList.size(); + for (size_t i = 0; i < typeCount; i++) { + const Type* const typeSpec = typeList[i]; + + int realEntryIndex = entryIndex; + int realTypeIndex = typeIndex; + bool currentTypeIsOverlay = false; + + // Runtime overlay packages provide a mapping of app resource + // ID to package resource ID. + if (typeSpec->idmapEntries.hasEntries()) { + uint16_t overlayEntryIndex; + if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) { + // No such mapping exists + continue; + } + realEntryIndex = overlayEntryIndex; + realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1; + currentTypeIsOverlay = true; + } - // Check to make sure this one is valid for the current parameters. - if (config && !thisConfig.match(*config)) { - TABLE_GETENTRY(ALOGI("Does not match config!\n")); + if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) { + ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)", + Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex), + entryIndex, static_cast<int>(typeSpec->entryCount)); + // We should normally abort here, but some legacy apps declare + // resources in the 'android' package (old bug in AAPT). continue; } - // Check if there is the desired entry in this type. + // Aggregate all the flags for each package that defines this entry. + if (typeSpec->typeSpecFlags != NULL) { + specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]); + } else { + specFlags = -1; + } - const uint8_t* const end = ((const uint8_t*)thisType) - + dtohl(thisType->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); + const size_t numConfigs = typeSpec->configs.size(); + for (size_t c = 0; c < numConfigs; c++) { + const ResTable_type* const thisType = typeSpec->configs[c]; + if (thisType == NULL) { + continue; + } - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n")); - continue; - } + ResTable_config thisConfig; + thisConfig.copyFromDtoH(thisType->config); - if (type != NULL) { - // Check if this one is less specific than the last found. If so, - // we will skip it. We check starting with things we most care - // about to those we least care about. - if (!thisConfig.isBetterThan(bestConfig, config)) { - TABLE_GETENTRY(ALOGI("This config is worse than last!\n")); + // Check to make sure this one is valid for the current parameters. + if (config != NULL && !thisConfig.match(*config)) { + continue; + } + + // Check if there is the desired entry in this type. + const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType) + + dtohl(thisType->header.size); + const uint32_t* const eindex = reinterpret_cast<const uint32_t*>( + reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[realEntryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + // There is no entry for this index and configuration. continue; } - } - type = thisType; - offset = thisOffset; - bestConfig = thisConfig; - TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n")); - if (!config) break; + if (bestType != NULL) { + // Check if this one is less specific than the last found. If so, + // we will skip it. We check starting with things we most care + // about to those we least care about. + if (!thisConfig.isBetterThan(bestConfig, config)) { + if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) { + continue; + } + } + } + + bestType = thisType; + bestOffset = thisOffset; + bestConfig = thisConfig; + bestPackage = typeSpec->package; + actualTypeIndex = realTypeIndex; + + // If no config was specified, any type will do, so skip + if (config == NULL) { + break; + } + } } - if (type == NULL) { - TABLE_GETENTRY(ALOGI("No value found for requested entry!\n")); + if (bestType == NULL) { return BAD_INDEX; } - offset += dtohl(type->entriesStart); - TABLE_NOISY(ALOGD("Looking in resource table %p, typeOff=%p, offset=%p", - package->header->header, (void*)(((const char*)type)-((const char*)package->header->header)), - (void*)offset)); + bestOffset += dtohl(bestType->entriesStart); - if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { + if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) { ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", - offset, dtohl(type->header.size)); + bestOffset, dtohl(bestType->header.size)); return BAD_TYPE; } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at 0x%x is not on an integer boundary", - offset); + if ((bestOffset & 0x3) != 0) { + ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset); return BAD_TYPE; } - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)type) + offset); + const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(bestType) + bestOffset); if (dtohs(entry->size) < sizeof(*entry)) { ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); return BAD_TYPE; } - *outType = type; - *outEntry = entry; - if (outTypeClass != NULL) { - *outTypeClass = allTypes; + if (outEntry != NULL) { + outEntry->entry = entry; + outEntry->config = bestConfig; + outEntry->type = bestType; + outEntry->specFlags = specFlags; + outEntry->package = bestPackage; + outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset); + outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index)); } - return offset + dtohs(entry->size); + return NO_ERROR; } status_t ResTable::parsePackage(const ResTable_package* const pkg, - const Header* const header, uint32_t idmap_id) + const Header* const header) { const uint8_t* base = (const uint8_t*)pkg; - status_t err = validate_chunk(&pkg->header, sizeof(*pkg), + status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset), header->dataEnd, "ResTable_package"); if (err != NO_ERROR) { return (mError=err); @@ -5494,89 +5520,88 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=BAD_TYPE); } - Package* package = NULL; - PackageGroup* group = NULL; - uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id); - // If at this point id == 0, pkg is an overlay package without a - // corresponding idmap. During regular usage, overlay packages are - // always loaded alongside their idmaps, but during idmap creation - // the package is temporarily loaded by itself. - if (id < 256) { - - package = new Package(this, header, pkg); - if (package == NULL) { - return (mError=NO_MEMORY); - } - - if (idmap_id == 0) { - err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), - header->dataEnd-(base+dtohl(pkg->typeStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } + uint32_t id = dtohl(pkg->id); + KeyedVector<uint8_t, IdmapEntries> idmapEntries; - err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), - header->dataEnd-(base+dtohl(pkg->keyStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } + if (header->resourceIDMap != NULL) { + uint8_t targetPackageId = 0; + status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries); + if (err != NO_ERROR) { + ALOGW("Overlay is broken"); + return (mError=err); } + id = targetPackageId; + } - if (id == 0) { - // This is a library so assign an ID - id = mNextPackageId++; - } + if (id >= 256) { + LOG_ALWAYS_FATAL("Package id out of range"); + return NO_ERROR; + } else if (id == 0) { + // This is a library so assign an ID + id = mNextPackageId++; + } - size_t idx = mPackageMap[id]; - if (idx == 0) { - idx = mPackageGroups.size()+1; + PackageGroup* group = NULL; + Package* package = new Package(this, header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } - char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; - strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); - group = new PackageGroup(this, String16(tmpName), id); - if (group == NULL) { - delete package; - return (mError=NO_MEMORY); - } + err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } - //printf("Adding new package id %d at index %d\n", id, idx); - err = mPackageGroups.add(group); - if (err < NO_ERROR) { - return (mError=err); - } - group->basePackage = package; + err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } - mPackageMap[id] = (uint8_t)idx; + size_t idx = mPackageMap[id]; + if (idx == 0) { + idx = mPackageGroups.size() + 1; - // Find all packages that reference this package - size_t N = mPackageGroups.size(); - for (size_t i = 0; i < N; i++) { - mPackageGroups[i]->dynamicRefTable.addMapping( - group->name, static_cast<uint8_t>(group->id)); - } - } else { - group = mPackageGroups.itemAt(idx-1); - if (group == NULL) { - return (mError=UNKNOWN_ERROR); - } + char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; + strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); + group = new PackageGroup(this, String16(tmpName), id); + if (group == NULL) { + delete package; + return (mError=NO_MEMORY); } - err = group->packages.add(package); + + //printf("Adding new package id %d at index %d\n", id, idx); + err = mPackageGroups.add(group); if (err < NO_ERROR) { return (mError=err); } + + mPackageMap[id] = static_cast<uint8_t>(idx); + + // Find all packages that reference this package + size_t N = mPackageGroups.size(); + for (size_t i = 0; i < N; i++) { + mPackageGroups[i]->dynamicRefTable.addMapping( + group->name, static_cast<uint8_t>(group->id)); + } } else { - LOG_ALWAYS_FATAL("Package id out of range"); - return NO_ERROR; + group = mPackageGroups.itemAt(idx - 1); + if (group == NULL) { + return (mError=UNKNOWN_ERROR); + } } + err = group->packages.add(package); + if (err < NO_ERROR) { + return (mError=err); + } // Iterate through all chunks. - size_t curPackage = 0; - const ResChunk_header* chunk = (const ResChunk_header*)(((const uint8_t*)pkg) + dtohs(pkg->header.headerSize)); @@ -5597,6 +5622,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } const size_t typeSpecSize = dtohl(typeSpec->header.size); + const size_t newEntryCount = dtohl(typeSpec->entryCount); LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", (void*)(base-(const uint8_t*)chunk), @@ -5605,12 +5631,11 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (void*)typeSpecSize)); // look for block overrun or int overflow when multiplying by 4 if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) - || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) + || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSpecSize)) { ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(typeSpec->header.headerSize) - +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), - (void*)typeSpecSize); + (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)), + (void*)typeSpecSize); return (mError=BAD_TYPE); } @@ -5619,21 +5644,36 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=BAD_TYPE); } - while (package->types.size() < typeSpec->id) { - package->types.add(NULL); - } - Type* t = package->types[typeSpec->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(typeSpec->entryCount)); - package->types.editItemAt(typeSpec->id-1) = t; - } else if (dtohl(typeSpec->entryCount) != t->entryCount) { - ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", - (int)dtohl(typeSpec->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); + if (newEntryCount > 0) { + uint8_t typeIndex = typeSpec->id - 1; + ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id); + if (idmapIndex >= 0) { + typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1; + } + + TypeList& typeList = group->types.editItemAt(typeIndex); + if (!typeList.isEmpty()) { + const Type* existingType = typeList[0]; + if (existingType->entryCount != newEntryCount && idmapIndex < 0) { + ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", + (int) newEntryCount, (int) existingType->entryCount); + // We should normally abort here, but some legacy apps declare + // resources in the 'android' package (old bug in AAPT). + } + } + + Type* t = new Type(header, package, newEntryCount); + t->typeSpec = typeSpec; + t->typeSpecFlags = (const uint32_t*)( + ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); + if (idmapIndex >= 0) { + t->idmapEntries = idmapEntries[idmapIndex]; + } + typeList.add(t); + group->largestTypeId = max(group->largestTypeId, typeSpec->id); + } else { + ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id); } - t->typeSpecFlags = (const uint32_t*)( - ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); - t->typeSpec = typeSpec; } else if (ctype == RES_TABLE_TYPE_TYPE) { const ResTable_type* type = (const ResTable_type*)(chunk); @@ -5644,50 +5684,69 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } const uint32_t typeSize = dtohl(type->header.size); + const size_t newEntryCount = dtohl(type->entryCount); LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", (void*)(base-(const uint8_t*)chunk), dtohs(type->header.type), dtohs(type->header.headerSize), (void*)typeSize)); - if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) - > typeSize) { + if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) + > typeSize) { ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.", - (void*)(dtohs(type->header.headerSize) - +(sizeof(uint32_t)*dtohl(type->entryCount))), - typeSize); + (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)), + typeSize); return (mError=BAD_TYPE); } - if (dtohl(type->entryCount) != 0 + + if (newEntryCount != 0 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.", dtohl(type->entriesStart), typeSize); return (mError=BAD_TYPE); } + if (type->id == 0) { ALOGW("ResTable_type has an id of 0."); return (mError=BAD_TYPE); } - while (package->types.size() < type->id) { - package->types.add(NULL); - } - Type* t = package->types[type->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(type->entryCount)); - package->types.editItemAt(type->id-1) = t; - } else if (dtohl(type->entryCount) != t->entryCount) { - ALOGW("ResTable_type entry count inconsistent: given %d, previously %d", - (int)dtohl(type->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); + if (newEntryCount > 0) { + uint8_t typeIndex = type->id - 1; + ssize_t idmapIndex = idmapEntries.indexOfKey(type->id); + if (idmapIndex >= 0) { + typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1; + } + + TypeList& typeList = group->types.editItemAt(typeIndex); + if (typeList.isEmpty()) { + ALOGE("No TypeSpec for type %d", type->id); + return (mError=BAD_TYPE); + } + + Type* t = typeList.editItemAt(typeList.size() - 1); + if (newEntryCount != t->entryCount) { + ALOGE("ResTable_type entry count inconsistent: given %d, previously %d", + (int)newEntryCount, (int)t->entryCount); + return (mError=BAD_TYPE); + } + + if (t->package != package) { + ALOGE("No TypeSpec for type %d", type->id); + return (mError=BAD_TYPE); + } + + t->configs.add(type); + + TABLE_GETENTRY( + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + ALOGI("Adding config to type %d: %s\n", + type->id, thisConfig.toString().string())); + } else { + ALOGV("Skipping empty ResTable_type for type %d", type->id); } - TABLE_GETENTRY( - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: %s\n", - type->id, thisConfig.toString().string())); - t->configs.add(type); } else if (ctype == RES_TABLE_LIBRARY_TYPE) { if (group->dynamicRefTable.entries().size() == 0) { status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk); @@ -5714,10 +5773,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (((const uint8_t*)chunk) + csize); } - if (group->typeCount == 0) { - group->typeCount = package->types.size(); - } - return NO_ERROR; } @@ -5818,6 +5873,12 @@ status_t DynamicRefTable::lookupResourceValue(Res_value* value) const { return NO_ERROR; } +struct IdmapTypeMap { + ssize_t overlayTypeId; + size_t entryOffset; + Vector<uint32_t> entryMap; +}; + status_t ResTable::createIdmap(const ResTable& overlay, uint32_t targetCrc, uint32_t overlayCrc, const char* targetPath, const char* overlayPath, @@ -5828,41 +5889,46 @@ status_t ResTable::createIdmap(const ResTable& overlay, ALOGW("idmap: target package has no package groups, cannot create idmap\n"); return UNKNOWN_ERROR; } + if (mPackageGroups[0]->packages.size() == 0) { ALOGW("idmap: target package has no packages in its first package group, " "cannot create idmap\n"); return UNKNOWN_ERROR; } - Vector<Vector<uint32_t> > map; + KeyedVector<uint8_t, IdmapTypeMap> map; + // overlaid packages are assumed to contain only one package group const PackageGroup* pg = mPackageGroups[0]; - const Package* pkg = pg->packages[0]; - size_t typeCount = pkg->types.size(); - // starting size is header + first item (number of types in map) - *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t); + + // starting size is header + *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; + + // target package id and number of types in map + *outSize += 2 * sizeof(uint16_t); + // overlay packages are assumed to contain only one package group const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name); - const uint32_t pkg_id = pkg->package->id << 24; - - for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) { - ssize_t first = -1; - ssize_t last = -1; - const Type* typeConfigs = pkg->getType(typeIndex); - ssize_t mapIndex = map.add(); - if (mapIndex < 0) { - return NO_MEMORY; + + for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) { + const TypeList& typeList = pg->types[typeIndex]; + if (typeList.isEmpty()) { + continue; } - Vector<uint32_t>& vector = map.editItemAt(mapIndex); + + const Type* typeConfigs = typeList[0]; + + IdmapTypeMap typeMap; + typeMap.overlayTypeId = -1; + typeMap.entryOffset = 0; + for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) { - uint32_t resID = pkg_id - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); + uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex); resource_name resName; if (!this->getResourceName(resID, false, &resName)) { - ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); - // add dummy value, or trimming leading/trailing zeroes later will fail - vector.push(0); + if (typeMap.entryMap.isEmpty()) { + typeMap.entryOffset++; + } continue; } @@ -5874,49 +5940,55 @@ status_t ResTable::createIdmap(const ResTable& overlay, overlayType.size(), overlayPackage.string(), overlayPackage.size()); - if (overlayResID != 0) { - overlayResID = pkg_id | (0x00ffffff & overlayResID); - last = Res_GETENTRY(resID); - if (first == -1) { - first = Res_GETENTRY(resID); + if (overlayResID == 0) { + if (typeMap.entryMap.isEmpty()) { + typeMap.entryOffset++; } + continue; } - vector.push(overlayResID); -#if 0 - if (overlayResID != 0) { - ALOGD("%s/%s 0x%08x -> 0x%08x\n", - String8(String16(resName.type)).string(), - String8(String16(resName.name)).string(), - resID, overlayResID); + + if (typeMap.overlayTypeId == -1) { + typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1; + } + + if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) { + ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x" + " but entries should map to resources of type %02x", + resID, overlayResID, typeMap.overlayTypeId); + return BAD_TYPE; + } + + if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) { + // Resize to accomodate this entry and the 0's in between. + if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) { + return NO_MEMORY; + } + typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID); + } else { + typeMap.entryMap.add(Res_GETENTRY(overlayResID)); } -#endif } - if (first != -1) { - // shave off trailing entries which lack overlay values - const size_t last_past_one = last + 1; - if (last_past_one < vector.size()) { - vector.removeItemsAt(last_past_one, vector.size() - last_past_one); + if (!typeMap.entryMap.isEmpty()) { + if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) { + return NO_MEMORY; } - // shave off leading entries which lack overlay values - vector.removeItemsAt(0, first); - // store offset to first overlaid resource ID of this type - vector.insertAt((uint32_t)first, 0, 1); - // reserve space for number and offset of entries, and the actual entries - *outSize += (2 + vector.size()) * sizeof(uint32_t); - } else { - // no entries of current type defined in overlay package - vector.clear(); - // reserve space for type offset - *outSize += 1 * sizeof(uint32_t); + *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t)); } } + if (map.isEmpty()) { + ALOGW("idmap: no resources in overlay package present in base package"); + return UNKNOWN_ERROR; + } + if ((*outData = malloc(*outSize)) == NULL) { return NO_MEMORY; } + uint32_t* data = (uint32_t*)*outData; *data++ = htodl(IDMAP_MAGIC); + *data++ = htodl(IDMAP_CURRENT_VERSION); *data++ = htodl(targetCrc); *data++ = htodl(overlayCrc); const char* paths[] = { targetPath, overlayPath }; @@ -5934,44 +6006,30 @@ status_t ResTable::createIdmap(const ResTable& overlay, data += 256 / sizeof(uint32_t); } const size_t mapSize = map.size(); - *data++ = htodl(mapSize); - size_t offset = mapSize; + uint16_t* typeData = reinterpret_cast<uint16_t*>(data); + *typeData++ = htods(pg->id); + *typeData++ = htods(mapSize); for (size_t i = 0; i < mapSize; ++i) { - const Vector<uint32_t>& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - *data++ = htodl(0); - } else { - offset++; - *data++ = htodl(offset); - offset += N; - } - } - if (offset == mapSize) { - ALOGW("idmap: no resources in overlay package present in base package\n"); - return UNKNOWN_ERROR; - } - for (size_t i = 0; i < mapSize; ++i) { - const Vector<uint32_t>& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - continue; - } - if (N == 1) { // vector expected to hold (offset) + (N > 0 entries) - ALOGW("idmap: type %u supposedly has entries, but no entries found\n", (uint32_t)i); - return UNKNOWN_ERROR; - } - *data++ = htodl(N - 1); // do not count the offset (which is vector's first element) - for (size_t j = 0; j < N; ++j) { - const uint32_t& overlayResID = vector.itemAt(j); - *data++ = htodl(overlayResID); + uint8_t targetTypeId = map.keyAt(i); + const IdmapTypeMap& typeMap = map[i]; + *typeData++ = htods(targetTypeId + 1); + *typeData++ = htods(typeMap.overlayTypeId); + *typeData++ = htods(typeMap.entryMap.size()); + *typeData++ = htods(typeMap.entryOffset); + + const size_t entryCount = typeMap.entryMap.size(); + uint32_t* entries = reinterpret_cast<uint32_t*>(typeData); + for (size_t j = 0; j < entryCount; j++) { + entries[j] = htodl(typeMap.entryMap[j]); } + typeData += entryCount * 2; } return NO_ERROR; } bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, + uint32_t* pVersion, uint32_t* pTargetCrc, uint32_t* pOverlayCrc, String8* pTargetPath, String8* pOverlayPath) { @@ -5979,17 +6037,20 @@ bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, if (!assertIdmapHeader(map, sizeBytes)) { return false; } + if (pVersion) { + *pVersion = dtohl(map[1]); + } if (pTargetCrc) { - *pTargetCrc = map[1]; + *pTargetCrc = dtohl(map[2]); } if (pOverlayCrc) { - *pOverlayCrc = map[2]; + *pOverlayCrc = dtohl(map[3]); } if (pTargetPath) { - pTargetPath->setTo(reinterpret_cast<const char*>(map + 3)); + pTargetPath->setTo(reinterpret_cast<const char*>(map + 4)); } if (pOverlayPath) { - pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t))); + pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t))); } return true; } @@ -6138,184 +6199,184 @@ void ResTable::print(bool inclValues) const size_t pkgCount = pg->packages.size(); for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) { const Package* pkg = pg->packages[pkgIndex]; - size_t typeCount = pkg->types.size(); - printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, - pkg->package->id, String8(String16(pkg->package->name)).string(), - (int)typeCount); - for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) { - const Type* typeConfigs = pkg->getType(typeIndex); - if (typeConfigs == NULL) { - printf(" type %d NULL\n", (int)typeIndex); + printf(" Package %d id=%d name=%s\n", (int)pkgIndex, + pkg->package->id, String8(String16(pkg->package->name)).string()); + } + + for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) { + const TypeList& typeList = pg->types[typeIndex]; + if (typeList.isEmpty()) { + //printf(" type %d NULL\n", (int)typeIndex); + continue; + } + const Type* typeConfigs = typeList[0]; + const size_t NTC = typeConfigs->configs.size(); + printf(" type %d configCount=%d entryCount=%d\n", + (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); + if (typeConfigs->typeSpecFlags != NULL) { + for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) { + uint32_t resID = (0xff000000 & ((pg->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + // Since we are creating resID without actually + // iterating over them, we have no idea which is a + // dynamic reference. We must check. + pg->dynamicRefTable.lookupResourceId(&resID); + + resource_name resName; + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); + } else { + name8 = String8(resName.name, resName.nameLen); + } + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + type8.string(), name8.string(), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } else { + printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + } + } + } + for (size_t configIndex=0; configIndex<NTC; configIndex++) { + const ResTable_type* type = typeConfigs->configs[configIndex]; + if ((((uint64_t)type)&0x3) != 0) { + printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - const size_t NTC = typeConfigs->configs.size(); - printf(" type %d configCount=%d entryCount=%d\n", - (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); - if (typeConfigs->typeSpecFlags != NULL) { - for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) { - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - // Since we are creating resID without actually - // iterating over them, we have no idea which is a - // dynamic reference. We must check. - pg->dynamicRefTable.lookupResourceId(&resID); - - resource_name resName; - if (this->getResourceName(resID, true, &resName)) { - String8 type8; - String8 name8; - if (resName.type8 != NULL) { - type8 = String8(resName.type8, resName.typeLen); - } else { - type8 = String8(resName.type, resName.typeLen); - } - if (resName.name8 != NULL) { - name8 = String8(resName.name8, resName.nameLen); - } else { - name8 = String8(resName.name, resName.nameLen); - } - printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", - resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - type8.string(), name8.string(), - dtohl(typeConfigs->typeSpecFlags[entryIndex])); + String8 configStr = type->config.toString(); + printf(" config %s:\n", configStr.size() > 0 + ? configStr.string() : "(default)"); + size_t entryCount = dtohl(type->entryCount); + uint32_t entriesStart = dtohl(type->entriesStart); + if ((entriesStart&0x3) != 0) { + printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart); + continue; + } + uint32_t typeSize = dtohl(type->header.size); + if ((typeSize&0x3) != 0) { + printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize); + continue; + } + for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { + + const uint8_t* const end = ((const uint8_t*)type) + + dtohl(type->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)type) + dtohs(type->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + continue; + } + + uint32_t resID = (0xff000000 & ((pg->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + pg->dynamicRefTable.lookupResourceId(&resID); + resource_name resName; + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); } else { - printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + name8 = String8(resName.name, resName.nameLen); } + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + type8.string(), name8.string()); + } else { + printf(" INVALID RESOURCE 0x%08x: ", resID); } - } - for (size_t configIndex=0; configIndex<NTC; configIndex++) { - const ResTable_type* type = typeConfigs->configs[configIndex]; - if ((((uint64_t)type)&0x3) != 0) { - printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); + if ((thisOffset&0x3) != 0) { + printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset); continue; } - String8 configStr = type->config.toString(); - printf(" config %s:\n", configStr.size() > 0 - ? configStr.string() : "(default)"); - size_t entryCount = dtohl(type->entryCount); - uint32_t entriesStart = dtohl(type->entriesStart); - if ((entriesStart&0x3) != 0) { - printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart); + if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { + printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n", + entriesStart, thisOffset, typeSize); continue; } - uint32_t typeSize = dtohl(type->header.size); - if ((typeSize&0x3) != 0) { - printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize); + + const ResTable_entry* ent = (const ResTable_entry*) + (((const uint8_t*)type) + entriesStart + thisOffset); + if (((entriesStart + thisOffset)&0x3) != 0) { + printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n", + (entriesStart + thisOffset)); continue; } - for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { - const uint8_t* const end = ((const uint8_t*)type) - + dtohl(type->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)type) + dtohs(type->header.headerSize)); + uintptr_t esize = dtohs(ent->size); + if ((esize&0x3) != 0) { + printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize); + continue; + } + if ((thisOffset+esize) > typeSize) { + printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n", + entriesStart, thisOffset, (void *)esize, typeSize); + continue; + } - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - continue; - } + const Res_value* valuePtr = NULL; + const ResTable_map_entry* bagPtr = NULL; + Res_value value; + if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { + printf("<bag>"); + bagPtr = (const ResTable_map_entry*)ent; + } else { + valuePtr = (const Res_value*) + (((const uint8_t*)ent) + esize); + value.copyFrom_dtoh(*valuePtr); + printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", + (int)value.dataType, (int)value.data, + (int)value.size, (int)value.res0); + } - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - pg->dynamicRefTable.lookupResourceId(&resID); - resource_name resName; - if (this->getResourceName(resID, true, &resName)) { - String8 type8; - String8 name8; - if (resName.type8 != NULL) { - type8 = String8(resName.type8, resName.typeLen); - } else { - type8 = String8(resName.type, resName.typeLen); - } - if (resName.name8 != NULL) { - name8 = String8(resName.name8, resName.nameLen); - } else { - name8 = String8(resName.name, resName.nameLen); + if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + printf(" (PUBLIC)"); + } + printf("\n"); + + if (inclValues) { + if (valuePtr != NULL) { + printf(" "); + print_value(typeConfigs->package, value); + } else if (bagPtr != NULL) { + const int N = dtohl(bagPtr->count); + const uint8_t* baseMapPtr = (const uint8_t*)ent; + size_t mapOffset = esize; + const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); + const uint32_t parent = dtohl(bagPtr->parent.ident); + uint32_t resolvedParent = parent; + status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent); + if (err != NO_ERROR) { + resolvedParent = 0; } - printf(" resource 0x%08x %s:%s/%s: ", resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - type8.string(), name8.string()); - } else { - printf(" INVALID RESOURCE 0x%08x: ", resID); - } - if ((thisOffset&0x3) != 0) { - printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset); - continue; - } - if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { - printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n", - entriesStart, thisOffset, typeSize); - continue; - } - - const ResTable_entry* ent = (const ResTable_entry*) - (((const uint8_t*)type) + entriesStart + thisOffset); - if (((entriesStart + thisOffset)&0x3) != 0) { - printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n", - (entriesStart + thisOffset)); - continue; - } - - uintptr_t esize = dtohs(ent->size); - if ((esize&0x3) != 0) { - printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize); - continue; - } - if ((thisOffset+esize) > typeSize) { - printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n", - entriesStart, thisOffset, (void *)esize, typeSize); - continue; - } - - const Res_value* valuePtr = NULL; - const ResTable_map_entry* bagPtr = NULL; - Res_value value; - if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { - printf("<bag>"); - bagPtr = (const ResTable_map_entry*)ent; - } else { - valuePtr = (const Res_value*) - (((const uint8_t*)ent) + esize); - value.copyFrom_dtoh(*valuePtr); - printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", - (int)value.dataType, (int)value.data, - (int)value.size, (int)value.res0); - } - - if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { - printf(" (PUBLIC)"); - } - printf("\n"); - - if (inclValues) { - if (valuePtr != NULL) { - printf(" "); - print_value(pkg, value); - } else if (bagPtr != NULL) { - const int N = dtohl(bagPtr->count); - const uint8_t* baseMapPtr = (const uint8_t*)ent; - size_t mapOffset = esize; - const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - const uint32_t parent = dtohl(bagPtr->parent.ident); - uint32_t resolvedParent = parent; - status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent); - if (err != NO_ERROR) { - resolvedParent = 0; - } - printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n", - parent, resolvedParent, N); - for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { - printf(" #%i (Key=0x%08x): ", - i, dtohl(mapPtr->name.ident)); - value.copyFrom_dtoh(mapPtr->value); - print_value(pkg, value); - const size_t size = dtohs(mapPtr->value.size); - mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); - mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - } + printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n", + parent, resolvedParent, N); + for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { + printf(" #%i (Key=0x%08x): ", + i, dtohl(mapPtr->name.ident)); + value.copyFrom_dtoh(mapPtr->value); + print_value(typeConfigs->package, value); + const size_t size = dtohs(mapPtr->value.size); + mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); + mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); } } } diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp new file mode 100644 index 0000000..06b4040 --- /dev/null +++ b/libs/androidfw/TypeWrappers.cpp @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#include <androidfw/TypeWrappers.h> + +namespace android { + +TypeVariant::iterator& TypeVariant::iterator::operator++() { + mIndex++; + if (mIndex > dtohl(mTypeVariant->data->entryCount)) { + mIndex = dtohl(mTypeVariant->data->entryCount); + } + return *this; +} + +const ResTable_entry* TypeVariant::iterator::operator*() const { + const ResTable_type* type = mTypeVariant->data; + const uint32_t entryCount = dtohl(type->entryCount); + if (mIndex >= entryCount) { + return NULL; + } + + const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type) + + dtohl(type->header.size); + const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>( + reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize)); + if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) > containerEnd) { + ALOGE("Type's entry indices extend beyond its boundaries"); + return NULL; + } + + const uint32_t entryOffset = dtohl(entryIndices[mIndex]); + if (entryOffset == ResTable_type::NO_ENTRY) { + return NULL; + } + + if ((entryOffset & 0x3) != 0) { + ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset); + return NULL; + } + + const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset); + if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) { + ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex); + return NULL; + } else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) { + ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex); + return NULL; + } else if (dtohs(entry->size) < sizeof(*entry)) { + ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size)); + return NULL; + } + return entry; +} + +} // namespace android diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 9e9649c..4ff6eec 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -1,33 +1,66 @@ -# Build the unit tests. +# +# 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. +# + +# ========================================================== +# Setup some common variables for the different build +# targets here. +# ========================================================== LOCAL_PATH:= $(call my-dir) +testFiles := \ + ByteBucketArray_test.cpp \ + Idmap_test.cpp \ + ResourceTypes_test.cpp \ + ResTable_test.cpp \ + Split_test.cpp \ + TypeWrappers_test.cpp \ + ZipUtils_test.cpp + +# ========================================================== +# Build the host tests: libandroidfw_tests +# ========================================================== +include $(CLEAR_VARS) + +LOCAL_MODULE := libandroidfw_tests + +LOCAL_SRC_FILES := $(testFiles) +LOCAL_STATIC_LIBRARIES := \ + libandroidfw \ + libutils \ + libcutils \ + liblog + +include $(BUILD_HOST_NATIVE_TEST) + + +# ========================================================== +# Build the device tests: libandroidfw_tests +# ========================================================== include $(CLEAR_VARS) -# Build the unit tests. -test_src_files := \ +LOCAL_MODULE := libandroidfw_tests + +LOCAL_SRC_FILES := $(testFiles) \ BackupData_test.cpp \ - ObbFile_test.cpp \ - ZipUtils_test.cpp \ - ResourceTypes_test.cpp + ObbFile_test.cpp -shared_libraries := \ +LOCAL_SHARED_LIBRARIES := \ libandroidfw \ libcutils \ libutils \ libui \ libstlport -static_libraries := \ - libgtest \ - libgtest_main - -$(foreach file,$(test_src_files), \ - $(eval include $(CLEAR_VARS)) \ - $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ - $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ - $(eval LOCAL_SRC_FILES := $(file)) \ - $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ - $(eval include $(BUILD_NATIVE_TEST)) \ -) - -# Build the manual test programs. -include $(call all-makefiles-under, $(LOCAL_PATH)) +include $(BUILD_NATIVE_TEST) diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp new file mode 100644 index 0000000..376e79c --- /dev/null +++ b/libs/androidfw/tests/ByteBucketArray_test.cpp @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#include <androidfw/ByteBucketArray.h> + +#include <gtest/gtest.h> + +using android::ByteBucketArray; + +TEST(ByteBucketArrayTest, TestSparseInsertion) { + ByteBucketArray<int> bba; + ASSERT_TRUE(bba.set(0, 1)); + ASSERT_TRUE(bba.set(10, 2)); + ASSERT_TRUE(bba.set(26, 3)); + ASSERT_TRUE(bba.set(129, 4)); + ASSERT_TRUE(bba.set(234, 5)); + + for (size_t i = 0; i < bba.size(); i++) { + switch (i) { + case 0: EXPECT_EQ(1, bba[i]); break; + case 10: EXPECT_EQ(2, bba[i]); break; + case 26: EXPECT_EQ(3, bba[i]); break; + case 129: EXPECT_EQ(4, bba[i]); break; + case 234: EXPECT_EQ(5, bba[i]); break; + default: EXPECT_EQ(0, bba[i]); break; + } + } +} diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp new file mode 100644 index 0000000..d829b76 --- /dev/null +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -0,0 +1,124 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> + +#include <utils/String8.h> +#include <utils/String16.h> +#include "TestHelpers.h" + +#include <gtest/gtest.h> + +using namespace android; + +namespace { + +/** + * Include a binary resource table. + * + * Package: com.android.test.basic + */ +#include "data/basic/basic_arsc.h" + +/** + * Include a binary resource table. + * This table is an overlay. + * + * Package: com.android.test.basic + */ +#include "data/overlay/overlay_arsc.h" + +enum { MAY_NOT_BE_BAG = false }; + +static const uint32_t attr_attr1 = 0x7f010000; +static const uint32_t attr_attr2 = 0x7f010001; +static const uint32_t string_test1 = 0x7f020000; +static const uint32_t string_test2 = 0x7f020001; +static const uint32_t integer_number1 = 0x7f030000; +static const uint32_t integer_number2 = 0x7f030001; +static const uint32_t style_Theme1 = 0x7f040000; +static const uint32_t style_Theme2 = 0x7f040001; +static const uint32_t array_integerArray1 = 0x7f050000; + +class IdmapTest : public ::testing::Test { +protected: + virtual void SetUp() { + ASSERT_EQ(NO_ERROR, mTargetTable.add(basic_arsc, basic_arsc_len)); + ASSERT_EQ(NO_ERROR, mOverlayTable.add(overlay_arsc, overlay_arsc_len)); + char targetName[256] = "com.android.test.basic"; + ASSERT_EQ(NO_ERROR, mTargetTable.createIdmap(mOverlayTable, 0, 0, + targetName, targetName, &mData, &mDataSize)); + } + + virtual void TearDown() { + free(mData); + } + + ResTable mTargetTable; + ResTable mOverlayTable; + void* mData; + size_t mDataSize; +}; + +TEST_F(IdmapTest, canLoadIdmap) { + ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); +} + +TEST_F(IdmapTest, overlayOverridesResourceValue) { + Res_value val; + ssize_t block = mTargetTable.getResource(string_test2, &val, false); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); + const ResStringPool* pool = mTargetTable.getTableStringBlock(block); + ASSERT_TRUE(pool != NULL); + ASSERT_LT(val.data, pool->size()); + + size_t strLen; + const char16_t* targetStr16 = pool->stringAt(val.data, &strLen); + ASSERT_TRUE(targetStr16 != NULL); + ASSERT_EQ(String16("test2"), String16(targetStr16, strLen)); + + ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); + + ssize_t newBlock = mTargetTable.getResource(string_test2, &val, false); + ASSERT_GE(newBlock, 0); + ASSERT_NE(block, newBlock); + ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); + pool = mTargetTable.getTableStringBlock(newBlock); + ASSERT_TRUE(pool != NULL); + ASSERT_LT(val.data, pool->size()); + + targetStr16 = pool->stringAt(val.data, &strLen); + ASSERT_TRUE(targetStr16 != NULL); + ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen)); +} + +TEST_F(IdmapTest, overlaidResourceHasSameName) { + ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize)); + + ResTable::resource_name resName; + ASSERT_TRUE(mTargetTable.getResourceName(array_integerArray1, false, &resName)); + + ASSERT_TRUE(resName.package != NULL); + ASSERT_TRUE(resName.type != NULL); + ASSERT_TRUE(resName.name != NULL); + + EXPECT_EQ(String16("com.android.test.basic"), String16(resName.package, resName.packageLen)); + EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen)); + EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen)); +} + +} // namespace diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp new file mode 100644 index 0000000..54d42c3 --- /dev/null +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -0,0 +1,189 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> + +#include <utils/String8.h> +#include <utils/String16.h> +#include "TestHelpers.h" + +#include <gtest/gtest.h> + +using namespace android; + +namespace { + +/** + * Include a binary resource table. + * + * Package: com.android.test.basic + */ +#include "data/basic/basic_arsc.h" + +enum { MAY_NOT_BE_BAG = false }; + +static const uint32_t attr_attr1 = 0x7f010000; +static const uint32_t attr_attr2 = 0x7f010001; +static const uint32_t string_test1 = 0x7f020000; +static const uint32_t string_test2 = 0x7f020001; +static const uint32_t integer_number1 = 0x7f030000; +static const uint32_t integer_number2 = 0x7f030001; +static const uint32_t style_Theme1 = 0x7f040000; +static const uint32_t style_Theme2 = 0x7f040001; +static const uint32_t array_integerArray1 = 0x7f050000; + +TEST(ResTableTest, shouldLoadSuccessfully) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); +} + +TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + Res_value val; + ssize_t block = table.getResource(string_test1, &val, MAY_NOT_BE_BAG); + + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); + + const ResStringPool* pool = table.getTableStringBlock(block); + ASSERT_TRUE(NULL != pool); + ASSERT_EQ(String8("test1"), pool->string8ObjectAt(val.data)); +} + +TEST(ResTableTest, resourceNameIsResolved) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + String16 defPackage("com.android.test.basic"); + String16 testName("@string/test1"); + uint32_t resID = table.identifierForName(testName.string(), testName.size(), + 0, 0, + defPackage.string(), defPackage.size()); + ASSERT_NE(uint32_t(0x00000000), resID); + ASSERT_EQ(string_test1, resID); +} + +TEST(ResTableTest, noParentThemeIsAppliedCorrectly) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + ResTable::Theme theme(table); + ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme1)); + + Res_value val; + uint32_t specFlags = 0; + ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(100), val.data); + + index = theme.getAttribute(attr_attr2, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(integer_number1, val.data); +} + +TEST(ResTableTest, parentThemeIsAppliedCorrectly) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + ResTable::Theme theme(table); + ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme2)); + + Res_value val; + uint32_t specFlags = 0; + ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(300), val.data); + + index = theme.getAttribute(attr_attr2, &val, &specFlags); + ASSERT_GE(index, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(integer_number1, val.data); +} + +TEST(ResTableTest, referenceToBagIsNotResolved) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + Res_value val; + ssize_t block = table.getResource(integer_number2, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + ASSERT_EQ(array_integerArray1, val.data); + + ssize_t newBlock = table.resolveReference(&val, block); + EXPECT_EQ(block, newBlock); + EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType); + EXPECT_EQ(array_integerArray1, val.data); +} + +TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + Res_value val; + ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + + const ResTable::bag_entry* entry; + ssize_t count = table.lockBag(array_integerArray1, &entry); + ASSERT_GE(count, 0); + table.unlockBag(entry); + + ResTable_config param; + memset(¶m, 0, sizeof(param)); + param.density = 320; + table.setParameters(¶m); + + block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + + count = table.lockBag(array_integerArray1, &entry); + ASSERT_GE(count, 0); + table.unlockBag(entry); +} + +TEST(ResTableTest, resourceIsOverridenWithBetterConfig) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len)); + + Res_value val; + ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG); + ASSERT_GE(block, 0); + ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType); + ASSERT_EQ(uint32_t(200), val.data); + + ResTable_config param; + memset(¶m, 0, sizeof(param)); + param.language[0] = 's'; + param.language[1] = 'v'; + param.country[0] = 'S'; + param.country[1] = 'E'; + table.setParameters(¶m); + + block = table.getResource(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); +} + +} diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp index 4888b4a..6041e08 100644 --- a/libs/androidfw/tests/ResourceTypes_test.cpp +++ b/libs/androidfw/tests/ResourceTypes_test.cpp @@ -64,8 +64,8 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) { config.packLanguage("eng"); // 1-00110-01 101-00100 - EXPECT_EQ(0x99, config.language[0]); - EXPECT_EQ(0xa4, config.language[1]); + EXPECT_EQ('\x99', config.language[0]); + EXPECT_EQ('\xA4', config.language[1]); char out[4] = { 1, 1, 1, 1}; config.unpackLanguage(out); diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp new file mode 100644 index 0000000..dbfdeae --- /dev/null +++ b/libs/androidfw/tests/Split_test.cpp @@ -0,0 +1,173 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> + +#include <utils/String8.h> +#include <utils/String16.h> +#include "TestHelpers.h" + +#include <gtest/gtest.h> + +/** + * Include a binary resource table. This table + * is a base table for an APK split. + * + * Package: com.android.example.split + * + * layout/main 0x7f020000 {default, fr-sw600dp-v13} + * + * string/app_title 0x7f030000 {default} + * string/test 0x7f030001 {default} + * string/boom 0x7f030002 {default} + * string/blah 0x7f030003 {default} + * + * array/lotsofstrings 0x7f040000 {default} + * array/numList 0x7f040001 {default} + * array/ary 0x7f040002 {default} + * + */ +#include "data/split_base_arsc.h" + +/** + * Include a binary resource table. This table + * is a configuration split table for an APK split. + * + * Package: com.android.example.split + * + * string/app_title 0x7f030000 {fr} + * string/test 0x7f030001 {de,fr} + * string/blah 0x7f030003 {fr} + * + * array/lotsofstrings 0x7f040000 {fr} + * + */ +#include "data/split_de_fr_arsc.h" + + +using namespace android; + +enum { MAY_NOT_BE_BAG = false }; + +void makeConfigFrench(ResTable_config* config) { + memset(config, 0, sizeof(*config)); + config->language[0] = 'f'; + config->language[1] = 'r'; +} + +TEST(SplitTest, TestLoadBase) { + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); +} + +TEST(SplitTest, TestGetResourceFromBase) { + ResTable_config frenchConfig; + makeConfigFrench(&frenchConfig); + + ResTable table; + table.setParameters(&frenchConfig); + + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); + + ResTable_config expectedConfig; + memset(&expectedConfig, 0, sizeof(expectedConfig)); + + Res_value val; + ResTable_config config; + ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config); + + // The returned block should tell us which string pool to get the value, if it is a string. + EXPECT_GE(block, 0); + + // We expect the default resource to be selected since it is the only resource configuration. + EXPECT_EQ(0, expectedConfig.compare(config)); + + EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); +} + +TEST(SplitTest, TestGetResourceFromSplit) { + ResTable_config expectedConfig; + makeConfigFrench(&expectedConfig); + + ResTable table; + table.setParameters(&expectedConfig); + + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); + ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + + Res_value val; + ResTable_config config; + ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config); + + EXPECT_GE(block, 0); + + EXPECT_EQ(0, expectedConfig.compare(config)); + + EXPECT_EQ(Res_value::TYPE_STRING, val.dataType); +} + +TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) { + ResTable_config expectedConfig; + makeConfigFrench(&expectedConfig); + + ResTable table; + table.setParameters(&expectedConfig); + + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); + + ResTable::resource_name baseName; + EXPECT_TRUE(table.getResourceName(0x7f030003, false, &baseName)); + + ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + + ResTable::resource_name frName; + EXPECT_TRUE(table.getResourceName(0x7f030003, false, &frName)); + + EXPECT_EQ( + String16(baseName.package, baseName.packageLen), + String16(frName.package, frName.packageLen)); + + EXPECT_EQ( + String16(baseName.type, baseName.typeLen), + String16(frName.type, frName.typeLen)); + + EXPECT_EQ( + String16(baseName.name, baseName.nameLen), + String16(frName.name, frName.nameLen)); +} + +TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) { + ResTable_config defaultConfig; + memset(&defaultConfig, 0, sizeof(defaultConfig)); + + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len)); + + Res_value val; + uint32_t specFlags = 0; + ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL); + EXPECT_GE(block, 0); + + EXPECT_EQ(static_cast<uint32_t>(0), specFlags); + + ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len)); + + uint32_t frSpecFlags = 0; + block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL); + EXPECT_GE(block, 0); + + EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags); +} diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h new file mode 100644 index 0000000..75a233a --- /dev/null +++ b/libs/androidfw/tests/TestHelpers.h @@ -0,0 +1,17 @@ +#ifndef __TEST_HELPERS_H +#define __TEST_HELPERS_H + +#include <ostream> + +#include <utils/String8.h> +#include <utils/String16.h> + +static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) { + return out << str.string(); +} + +static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) { + return out << android::String8(str).string(); +} + +#endif // __TEST_HELPERS_H diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp new file mode 100644 index 0000000..d69abe5 --- /dev/null +++ b/libs/androidfw/tests/TypeWrappers_test.cpp @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <androidfw/TypeWrappers.h> +#include <utils/String8.h> + +#include <gtest/gtest.h> + +namespace android { + +void* createTypeData() { + ResTable_type t; + memset(&t, 0, sizeof(t)); + t.header.type = RES_TABLE_TYPE_TYPE; + t.header.headerSize = sizeof(t); + t.id = 1; + t.entryCount = 3; + + uint32_t offsets[3]; + t.entriesStart = t.header.headerSize + sizeof(offsets); + t.header.size = t.entriesStart; + + offsets[0] = 0; + ResTable_entry e1; + memset(&e1, 0, sizeof(e1)); + e1.size = sizeof(e1); + e1.key.index = 0; + t.header.size += sizeof(e1); + + Res_value v1; + memset(&v1, 0, sizeof(v1)); + t.header.size += sizeof(v1); + + offsets[1] = ResTable_type::NO_ENTRY; + + offsets[2] = sizeof(e1) + sizeof(v1); + ResTable_entry e2; + memset(&e2, 0, sizeof(e2)); + e2.size = sizeof(e2); + e2.key.index = 1; + t.header.size += sizeof(e2); + + Res_value v2; + memset(&v2, 0, sizeof(v2)); + t.header.size += sizeof(v2); + + uint8_t* data = (uint8_t*)malloc(t.header.size); + uint8_t* p = data; + memcpy(p, &t, sizeof(t)); + p += sizeof(t); + memcpy(p, offsets, sizeof(offsets)); + p += sizeof(offsets); + memcpy(p, &e1, sizeof(e1)); + p += sizeof(e1); + memcpy(p, &v1, sizeof(v1)); + p += sizeof(v1); + memcpy(p, &e2, sizeof(e2)); + p += sizeof(e2); + memcpy(p, &v2, sizeof(v2)); + p += sizeof(v2); + return data; +} + +TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) { + ResTable_type* data = (ResTable_type*) createTypeData(); + + TypeVariant v(data); + + TypeVariant::iterator iter = v.beginEntries(); + ASSERT_EQ(uint32_t(0), iter.index()); + ASSERT_TRUE(NULL != *iter); + ASSERT_EQ(uint32_t(0), iter->key.index); + ASSERT_NE(v.endEntries(), iter); + + iter++; + + ASSERT_EQ(uint32_t(1), iter.index()); + ASSERT_TRUE(NULL == *iter); + ASSERT_NE(v.endEntries(), iter); + + iter++; + + ASSERT_EQ(uint32_t(2), iter.index()); + ASSERT_TRUE(NULL != *iter); + ASSERT_EQ(uint32_t(1), iter->key.index); + ASSERT_NE(v.endEntries(), iter); + + iter++; + + ASSERT_EQ(v.endEntries(), iter); + + free(data); +} + +} // namespace android diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore new file mode 100644 index 0000000..c05cfb0 --- /dev/null +++ b/libs/androidfw/tests/data/.gitignore @@ -0,0 +1,2 @@ +*.apk +*.arsc diff --git a/libs/androidfw/tests/data/basic/AndroidManifest.xml b/libs/androidfw/tests/data/basic/AndroidManifest.xml new file mode 100644 index 0000000..a56ac18 --- /dev/null +++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.basic"> + <application> + </application> +</manifest> diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h new file mode 100644 index 0000000..6532076 --- /dev/null +++ b/libs/androidfw/tests/data/basic/basic_arsc.h @@ -0,0 +1,131 @@ +unsigned char basic_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0xfc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 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, 0x02, 0x20, 0x01, 0xb0, 0x05, 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, 0x69, 0x00, 0x63, 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, 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, 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, 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, 0x20, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, + 0xdc, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x32, 0x00, 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, 0x07, 0x00, 0x6e, 0x00, + 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, 0x06, 0x00, 0x54, 0x00, + 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 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, + 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, 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, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x02, 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, 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, 0x02, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x03, 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, 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, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x7f, + 0x01, 0x02, 0x44, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x03, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x10, 0x90, 0x01, 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, 0x01, 0x02, 0x44, 0x00, + 0x90, 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, + 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, 0x28, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, + 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x03, 0x7f, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 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, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x30, 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, 0x08, 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 = 1532; diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build new file mode 100755 index 0000000..237342c --- /dev/null +++ b/libs/androidfw/tests/data/basic/build @@ -0,0 +1,6 @@ +#!/bin/bash + +aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \ +unzip bundle.apk resources.arsc && \ +mv resources.arsc basic.arsc && \ +xxd -i basic.arsc > basic_arsc.h diff --git a/libs/androidfw/tests/data/basic/res/values-sv/values.xml b/libs/androidfw/tests/data/basic/res/values-sv/values.xml new file mode 100644 index 0000000..9d52307 --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/values-sv/values.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <integer name="number1">400</integer> +</resources> diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml new file mode 100644 index 0000000..662eda6 --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/values/values.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <attr name="attr1" format="reference|integer" /> + <attr name="attr2" format="reference|integer" /> + + <string name="test1">test1</string> + <string name="test2">test2</string> + + <integer name="number1">200</integer> + <integer name="number2">@array/integerArray1</integer> + + <style name="Theme1"> + <item name="com.android.test.basic:attr1">100</item> + <item name="com.android.test.basic:attr2">@integer/number1</item> + </style> + + <style name="Theme2" parent="@com.android.test.basic:style/Theme1"> + <item name="com.android.test.basic:attr1">300</item> + </style> + + <integer-array name="integerArray1"> + <item>1</item> + <item>2</item> + <item>3</item> + </integer-array> +</resources> diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml new file mode 100644 index 0000000..a56ac18 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.basic"> + <application> + </application> +</manifest> diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build new file mode 100755 index 0000000..87cf6de --- /dev/null +++ b/libs/androidfw/tests/data/overlay/build @@ -0,0 +1,6 @@ +#!/bin/bash + +aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \ +unzip bundle.apk resources.arsc && \ +mv resources.arsc overlay.arsc && \ +xxd -i overlay.arsc > overlay_arsc.h diff --git a/libs/androidfw/tests/data/overlay/overlay_arsc.h b/libs/androidfw/tests/data/overlay/overlay_arsc.h new file mode 100644 index 0000000..5bd98b2 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/overlay_arsc.h @@ -0,0 +1,69 @@ +unsigned char overlay_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x40, 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, 0x0d, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x6f, 0x00, + 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xc4, 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, 0x69, 0x00, 0x63, 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, 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, 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, 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, 0x20, 0x01, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, + 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 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, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, + 0x0b, 0x00, 0x00, 0x00 +}; +unsigned int overlay_arsc_len = 784; diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml new file mode 100644 index 0000000..227e889 --- /dev/null +++ b/libs/androidfw/tests/data/overlay/res/values/values.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="test2">test2-overlay</string> + <integer-array name="integerArray1"> + <item>10</item> + <item>11</item> + </integer-array> +</resources> diff --git a/libs/androidfw/tests/data/split_base_arsc.h b/libs/androidfw/tests/data/split_base_arsc.h new file mode 100644 index 0000000..e0321e9 --- /dev/null +++ b/libs/androidfw/tests/data/split_base_arsc.h @@ -0,0 +1,221 @@ +unsigned char split_base_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x30, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x94, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, + 0xf4, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x2a, 0x01, 0x00, 0x00, + 0x44, 0x01, 0x00, 0x00, 0x13, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, + 0x74, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x22, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, + 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x66, 0x00, + 0x72, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x77, 0x00, 0x36, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x64, 0x00, 0x70, 0x00, 0x2d, 0x00, 0x76, 0x00, 0x31, 0x00, + 0x33, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x53, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, + 0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x00, + 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x2c, 0x00, 0x20, 0x00, + 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x42, 0x00, 0x6c, 0x00, 0x61, 0x00, + 0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x0b, 0x00, + 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, + 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x20, 0x00, + 0x62, 0x00, 0x79, 0x00, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x49, 0x00, + 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x41, 0x00, + 0x4c, 0x00, 0x4c, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x49, 0x00, + 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x31, 0x00, + 0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x20, 0x00, 0x3a, 0x00, 0x29, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x49, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x6f, 0x00, + 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x3a, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01, + 0x90, 0x08, 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, 0x65, 0x00, 0x78, 0x00, + 0x61, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00, + 0x73, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 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, 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, 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, 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, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, + 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x70, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00, + 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0x30, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00, + 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, + 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, + 0x4c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x00, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x00, 0x00, 0x03, 0x00, 0x71, 0x00, + 0x75, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x67, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x77, 0x00, + 0x69, 0x00, 0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x66, 0x00, 0x69, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 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, 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, 0x00, 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, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x94, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x30, 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, + 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, + 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0xc8, 0x00, 0x00, 0x00, + 0x04, 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, 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, 0x28, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, + 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x10, 0xd2, 0x04, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x7f, + 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x7f, + 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x05, 0x01, 0x19, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 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, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 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, + 0x58, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 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, + 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x12, 0xff, 0xff, 0xff, 0xff, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 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, + 0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x1d, 0x00, 0xff, 0x00, 0xff, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x30, 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, + 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x01, 0x17, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, + 0x01, 0xe6, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 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, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, + 0x0b, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 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, 0x0d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, + 0x7b, 0x00, 0x00, 0x00 +}; +unsigned int split_base_arsc_len = 2608; diff --git a/libs/androidfw/tests/data/split_de_fr_arsc.h b/libs/androidfw/tests/data/split_de_fr_arsc.h new file mode 100644 index 0000000..6f6a416 --- /dev/null +++ b/libs/androidfw/tests/data/split_de_fr_arsc.h @@ -0,0 +1,118 @@ +unsigned char split_de_fr_arsc[] = { + 0x02, 0x00, 0x0c, 0x00, 0x64, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x41, 0x00, 0x63, 0x00, 0x68, 0x00, + 0x74, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x20, 0x00, 0x44, 0x00, + 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0xe9, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6a, 0x00, 0x6f, 0x00, + 0x75, 0x00, 0x72, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x6f, 0x00, + 0x6e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x21, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x42, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x48, 0x00, 0xe9, 0x00, 0x20, 0x00, 0x6c, 0x00, + 0xe0, 0x00, 0x00, 0x00, 0x09, 0x00, 0x41, 0x00, 0x75, 0x00, 0x20, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01, 0xa0, 0x04, 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, 0x65, 0x00, 0x78, 0x00, 0x61, 0x00, 0x6d, 0x00, + 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x73, 0x00, 0x70, 0x00, + 0x6c, 0x00, 0x69, 0x00, 0x74, 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, + 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, 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, 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, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, + 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x67, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, + 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00, 0x70, 0x00, 0x6c, 0x00, + 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x65, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, + 0x78, 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, 0x22, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, + 0x5f, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00, + 0x6f, 0x00, 0x66, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x67, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x30, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x54, 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, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x44, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 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, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 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, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned int split_de_fr_arsc_len = 1380; diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 233f3f0..d578ef5 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1118,8 +1118,8 @@ public: const DeferredDisplayState& state) { DrawStrokableOp::onDefer(renderer, deferInfo, state); if (!mPaint->getPathEffect()) { - renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, - mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy, mPaint); + renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint, + mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy); } } diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 0e47c6e..fc4d40b 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -308,6 +308,10 @@ status_t DisplayListRenderer::drawOval(float left, float top, float right, float status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { + if (fabs(sweepAngle) > 360.0f) { + return drawOval(left, top, right, bottom, paint); + } + paint = refPaint(paint); addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint)); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 8f3872a..6397478 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2556,8 +2556,8 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float return drawShape(left, top, texture, p); } - const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(), - right - left, bottom - top, rx, ry, p); + const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect( + *currentTransform(), *p, right - left, bottom - top, rx, ry); return drawVertexBuffer(left, top, *vertexBuffer, p); } @@ -2611,10 +2611,6 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto return DrawGlInfo::kStatusDone; } - if (fabs(sweepAngle) >= 360.0f) { - return drawOval(left, top, right, bottom, p); - } - // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) { mCaches.activeTexture(0); diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 05f4edf..3d93383 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -20,6 +20,7 @@ #include "RenderNode.h" #include <algorithm> +#include <string> #include <SkCanvas.h> #include <algorithm> @@ -158,7 +159,10 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { applyLayerPropertiesToLayer(info); damageSelf(info); } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) { - LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight()); + if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) { + LayerRenderer::destroyLayer(mLayer); + mLayer = 0; + } damageSelf(info); } @@ -166,6 +170,15 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { info.damageAccumulator->peekAtDirty(&dirty); info.damageAccumulator->popTransform(); + if (!mLayer) { + if (info.errorHandler) { + std::string msg = "Unable to create layer for "; + msg += getName(); + info.errorHandler->onError(msg); + } + return; + } + if (!dirty.isEmpty()) { mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom); } diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index ef3d0d7..08b54ff 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -39,6 +39,8 @@ namespace uirenderer { TessellationCache::Description::Description() : type(kNone) + , scaleX(1.0f) + , scaleY(1.0f) , aa(false) , cap(SkPaint::kDefault_Cap) , style(SkPaint::kFill_Style) @@ -46,21 +48,13 @@ TessellationCache::Description::Description() memset(&shape, 0, sizeof(Shape)); } -TessellationCache::Description::Description(Type type) +TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint) : type(type) - , aa(false) - , cap(SkPaint::kDefault_Cap) - , style(SkPaint::kFill_Style) - , strokeWidth(1.0f) { - memset(&shape, 0, sizeof(Shape)); -} - -TessellationCache::Description::Description(Type type, const SkPaint* paint) - : type(type) - , aa(paint->isAntiAlias()) - , cap(paint->getStrokeCap()) - , style(paint->getStyle()) - , strokeWidth(paint->getStrokeWidth()) { + , aa(paint.isAntiAlias()) + , cap(paint.getStrokeCap()) + , style(paint.getStyle()) + , strokeWidth(paint.getStrokeWidth()) { + PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); memset(&shape, 0, sizeof(Shape)); } @@ -70,10 +64,20 @@ hash_t TessellationCache::Description::hash() const { hash = JenkinsHashMix(hash, cap); hash = JenkinsHashMix(hash, style); hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); + hash = JenkinsHashMix(hash, android::hash_type(scaleX)); + hash = JenkinsHashMix(hash, android::hash_type(scaleY)); hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); return JenkinsHashWhiten(hash); } +void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const { + matrix->loadScale(scaleX, scaleY, 1.0f); + paint->setAntiAlias(aa); + paint->setStrokeCap(cap); + paint->setStyle(style); + paint->setStrokeWidth(strokeWidth); +} + TessellationCache::ShadowDescription::ShadowDescription() : nodeKey(NULL) { memset(&matrixData, 0, 16 * sizeof(float)); @@ -96,20 +100,15 @@ hash_t TessellationCache::ShadowDescription::hash() const { class TessellationCache::TessellationTask : public Task<VertexBuffer*> { public: - TessellationTask(Tessellator tessellator, const Description& description, - const SkPaint* paint) + TessellationTask(Tessellator tessellator, const Description& description) : tessellator(tessellator) - , description(description) - , paint(*paint) { + , description(description) { } ~TessellationTask() {} Tessellator tessellator; Description description; - - //copied, since input paint may not be immutable - const SkPaint paint; }; class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> { @@ -121,7 +120,7 @@ public: virtual void onProcess(const sp<Task<VertexBuffer*> >& task) { TessellationTask* t = static_cast<TessellationTask*>(task.get()); ATRACE_NAME("shape tessellation"); - VertexBuffer* buffer = t->tessellator(t->description, t->paint); + VertexBuffer* buffer = t->tessellator(t->description); t->setResult(buffer); } }; @@ -416,21 +415,12 @@ void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rec // Tessellation precaching /////////////////////////////////////////////////////////////////////////////// -static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint, - float scaleX, float scaleY) { - VertexBuffer* buffer = new VertexBuffer(); - Matrix4 matrix; - matrix.loadScale(scaleX, scaleY, 1); - PathTessellator::tessellatePath(path, paint, matrix, *buffer); - return buffer; -} - TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( - const Description& entry, Tessellator tessellator, const SkPaint* paint) { + const Description& entry, Tessellator tessellator) { Buffer* buffer = mCache.get(entry); if (!buffer) { // not cached, enqueue a task to fill the buffer - sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint); + sp<TessellationTask> task = new TessellationTask(tessellator, entry); buffer = new Buffer(task); if (mProcessor == NULL) { @@ -442,43 +432,49 @@ TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( return buffer; } +static VertexBuffer* tessellatePath(const TessellationCache::Description& description, + const SkPath& path) { + Matrix4 matrix; + SkPaint paint; + description.setupMatrixAndPaint(&matrix, &paint); + VertexBuffer* buffer = new VertexBuffer(); + PathTessellator::tessellatePath(path, &paint, matrix, *buffer); + return buffer; +} + /////////////////////////////////////////////////////////////////////////////// -// Rounded rects +// RoundRect /////////////////////////////////////////////////////////////////////////////// -static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description, - const SkPaint& paint) { - SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth, - description.shape.roundRect.mHeight); - float rx = description.shape.roundRect.mRx; - float ry = description.shape.roundRect.mRy; - if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) { - float outset = paint.getStrokeWidth() / 2; +static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) { + SkRect rect = SkRect::MakeWH(description.shape.roundRect.width, + description.shape.roundRect.height); + float rx = description.shape.roundRect.rx; + float ry = description.shape.roundRect.ry; + if (description.style == SkPaint::kStrokeAndFill_Style) { + float outset = description.strokeWidth / 2; rect.outset(outset, outset); rx += outset; ry += outset; } SkPath path; path.addRoundRect(rect, rx, ry); - return tessellatePath(path, &paint, - description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY); + return tessellatePath(description, path); } -TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - Description entry(Description::kRoundRect, paint); - entry.shape.roundRect.mWidth = width; - entry.shape.roundRect.mHeight = height; - entry.shape.roundRect.mRx = rx; - entry.shape.roundRect.mRy = ry; - PathTessellator::extractTessellationScales(transform, - &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY); - - return getOrCreateBuffer(entry, &tessellateRoundRect, paint); +TessellationCache::Buffer* TessellationCache::getRoundRectBuffer( + const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + Description entry(Description::kRoundRect, transform, paint); + entry.shape.roundRect.width = width; + entry.shape.roundRect.height = height; + entry.shape.roundRect.rx = rx; + entry.shape.roundRect.ry = ry; + return getOrCreateBuffer(entry, &tessellateRoundRect); } -const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer(); +const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer(); } }; // namespace uirenderer diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index d4ff943..688a699 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -50,30 +50,28 @@ public: enum Type { kNone, kRoundRect, - kAmbientShadow, - kSpotShadow }; Type type; + float scaleX; + float scaleY; bool aa; SkPaint::Cap cap; SkPaint::Style style; float strokeWidth; union Shape { struct RoundRect { - float mScaleX; - float mScaleY; - float mWidth; - float mHeight; - float mRx; - float mRy; + float width; + float height; + float rx; + float ry; } roundRect; } shape; Description(); - Description(Type type); - Description(Type type, const SkPaint* paint); + Description(Type type, const Matrix4& transform, const SkPaint& paint); hash_t hash() const; + void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const; }; struct ShadowDescription { @@ -123,12 +121,12 @@ public: // TODO: precache/get for Oval, Lines, Points, etc. - void precacheRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - getRoundRectBuffer(transform, width, height, rx, ry, paint); + void precacheRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + getRoundRectBuffer(transform, paint, width, height, rx, ry); } - const VertexBuffer* getRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint); + const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry); void precacheShadows(const Matrix4* drawTransform, const Rect& localClip, bool opaque, const SkPath* casterPerimeter, @@ -146,14 +144,14 @@ private: class TessellationTask; class TessellationProcessor; + typedef VertexBuffer* (*Tessellator)(const Description&); - typedef VertexBuffer* (*Tessellator)(const Description&, const SkPaint&); + Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint, + float width, float height); + Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry); - Buffer* getRoundRectBuffer(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint); - - Buffer* getOrCreateBuffer(const Description& entry, - Tessellator tessellator, const SkPaint* paint); + Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator); uint32_t mSize; uint32_t mMaxSize; diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 0fc0cef..f67e434 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -16,6 +16,8 @@ #ifndef TREEINFO_H #define TREEINFO_H +#include <string> + #include <utils/Timers.h> #include "DamageAccumulator.h" @@ -35,6 +37,13 @@ protected: ~AnimationHook() {} }; +class ErrorHandler { +public: + virtual void onError(const std::string& message) = 0; +protected: + ~ErrorHandler() {} +}; + // This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN class TreeInfo { PREVENT_COPY_AND_ASSIGN(TreeInfo); @@ -65,6 +74,7 @@ public: , prepareTextures(mode == MODE_FULL) , damageAccumulator(NullDamageAccumulator::instance()) , renderer(0) + , errorHandler(0) {} const TraversalMode mode; @@ -78,6 +88,7 @@ public: // The renderer that will be drawing the next frame. Use this to push any // layer updates or similar. May be NULL. OpenGLRenderer* renderer; + ErrorHandler* errorHandler; struct Out { Out() diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h index 9a211a2..30b6ff2 100644 --- a/libs/hwui/thread/Task.h +++ b/libs/hwui/thread/Task.h @@ -17,8 +17,6 @@ #ifndef ANDROID_HWUI_TASK_H #define ANDROID_HWUI_TASK_H -#define ATRACE_TAG ATRACE_TAG_VIEW - #include <utils/RefBase.h> #include <utils/Trace.h> @@ -40,7 +38,7 @@ public: virtual ~Task() { } T getResult() const { - ATRACE_NAME("waitForTask"); + ScopedTrace tracer(ATRACE_TAG_VIEW, "waitForTask"); return mFuture->get(); } diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index e05aef0..bd2be1b 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -25,10 +25,10 @@ import java.lang.annotation.RetentionPolicy; * The AudioFormat class is used to access a number of audio format and * channel configuration constants. They are for instance used * in {@link AudioTrack} and {@link AudioRecord}. - * + * */ public class AudioFormat { - + //--------------------------------------------------------- // Constants //-------------------- @@ -44,6 +44,10 @@ public class AudioFormat { public static final int ENCODING_PCM_8BIT = 3; /** Audio data format: single-precision floating-point per sample */ public static final int ENCODING_PCM_FLOAT = 4; + /** Audio data format: AC-3 compressed */ + public static final int ENCODING_AC3 = 5; + /** Audio data format: E-AC-3 compressed */ + public static final int ENCODING_E_AC3 = 6; /** Invalid audio channel configuration */ /** @deprecated use CHANNEL_INVALID instead */ @@ -152,11 +156,44 @@ public class AudioFormat { switch (audioFormat) { case ENCODING_PCM_8BIT: return 1; + case ENCODING_PCM_16BIT: + case ENCODING_DEFAULT: + return 2; case ENCODING_PCM_FLOAT: return 4; + case ENCODING_INVALID: + default: + throw new IllegalArgumentException("Bad audio format " + audioFormat); + } + } + + /** @hide */ + public static boolean isValidEncoding(int audioFormat) + { + switch (audioFormat) { + case ENCODING_PCM_8BIT: case ENCODING_PCM_16BIT: + case ENCODING_PCM_FLOAT: + case ENCODING_AC3: + case ENCODING_E_AC3: + return true; + default: + return false; + } + } + + /** @hide */ + public static boolean isEncodingLinearPcm(int audioFormat) + { + switch (audioFormat) { + case ENCODING_PCM_8BIT: + case ENCODING_PCM_16BIT: + case ENCODING_PCM_FLOAT: case ENCODING_DEFAULT: - return 2; + return true; + case ENCODING_AC3: + case ENCODING_E_AC3: + return false; case ENCODING_INVALID: default: throw new IllegalArgumentException("Bad audio format " + audioFormat); @@ -236,7 +273,9 @@ public class AudioFormat { * @param encoding one of {@link AudioFormat#ENCODING_DEFAULT}, * {@link AudioFormat#ENCODING_PCM_8BIT}, * {@link AudioFormat#ENCODING_PCM_16BIT}, - * {@link AudioFormat#ENCODING_PCM_FLOAT}. + * {@link AudioFormat#ENCODING_PCM_FLOAT}, + * {@link AudioFormat#ENCODING_AC3}, + * {@link AudioFormat#ENCODING_E_AC3}. * @return the same Builder instance. * @throws java.lang.IllegalArgumentException */ @@ -248,6 +287,8 @@ public class AudioFormat { case ENCODING_PCM_8BIT: case ENCODING_PCM_16BIT: case ENCODING_PCM_FLOAT: + case ENCODING_AC3: + case ENCODING_E_AC3: mEncoding = encoding; break; case ENCODING_INVALID: @@ -311,7 +352,9 @@ public class AudioFormat { ENCODING_DEFAULT, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, - ENCODING_PCM_FLOAT + ENCODING_PCM_FLOAT, + ENCODING_AC3, + ENCODING_E_AC3 }) @Retention(RetentionPolicy.SOURCE) public @interface Encoding {} diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 2f1e11e..cf6d35e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2534,6 +2534,9 @@ public class AudioManager { // from AudioManager. AudioSystem is an internal class used by AudioManager and AudioService. /** @hide + * The audio device code for representing "no device." */ + public static final int DEVICE_NONE = AudioSystem.DEVICE_NONE; + /** @hide * The audio output device code for the small speaker at the front of the device used * when placing calls. Does not refer to an in-ear headphone without attached microphone, * such as earbuds, earphones, or in-ear monitors (IEM). Those would be handled as a @@ -2971,7 +2974,9 @@ public class AudioManager { /** @hide */ public static final int ERROR_NO_INIT = AudioSystem.NO_INIT; - /** @hide + /** + * An error code indicating that the object reporting it is no longer valid and needs to + * be recreated. */ public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT; diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 9fbcd18..63ed10c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -225,6 +225,7 @@ public class AudioSystem // audio device definitions: must be kept in sync with values in system/core/audio.h // + public static final int DEVICE_NONE = 0x0; // reserved bits public static final int DEVICE_BIT_IN = 0x80000000; public static final int DEVICE_BIT_DEFAULT = 0x40000000; diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index cfd9c3b..3a72833 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -457,25 +457,19 @@ public class AudioTrack //-------------- // audio format - switch (audioFormat) { - case AudioFormat.ENCODING_DEFAULT: - mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; - break; - case AudioFormat.ENCODING_PCM_16BIT: - case AudioFormat.ENCODING_PCM_8BIT: - case AudioFormat.ENCODING_PCM_FLOAT: - mAudioFormat = audioFormat; - break; - default: - throw new IllegalArgumentException("Unsupported sample encoding." - + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT" - + " or ENCODING_PCM_FLOAT" - + "."); + if (audioFormat == AudioFormat.ENCODING_DEFAULT) { + audioFormat = AudioFormat.ENCODING_PCM_16BIT; + } + + if (!AudioFormat.isValidEncoding(audioFormat)) { + throw new IllegalArgumentException("Unsupported audio encoding."); } + mAudioFormat = audioFormat; //-------------- // audio load mode - if ( (mode != MODE_STREAM) && (mode != MODE_STATIC) ) { + if (((mode != MODE_STREAM) && (mode != MODE_STATIC)) || + ((mode != MODE_STREAM) && !AudioFormat.isEncodingLinearPcm(mAudioFormat))) { throw new IllegalArgumentException("Invalid mode."); } mDataLoadMode = mode; @@ -522,8 +516,13 @@ public class AudioTrack private void audioBuffSizeCheck(int audioBufferSize) { // NB: this section is only valid with PCM data. // To update when supporting compressed formats - int frameSizeInBytes = mChannelCount - * (AudioFormat.getBytesPerSample(mAudioFormat)); + int frameSizeInBytes; + if (AudioFormat.isEncodingLinearPcm(mAudioFormat)) { + frameSizeInBytes = mChannelCount + * (AudioFormat.getBytesPerSample(mAudioFormat)); + } else { + frameSizeInBytes = 1; + } if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { throw new IllegalArgumentException("Invalid audio buffer size."); } @@ -757,9 +756,7 @@ public class AudioTrack } } - if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) - && (audioFormat != AudioFormat.ENCODING_PCM_8BIT) - && (audioFormat != AudioFormat.ENCODING_PCM_FLOAT)) { + if (!AudioFormat.isValidEncoding(audioFormat)) { loge("getMinBufferSize(): Invalid audio format."); return ERROR_BAD_VALUE; } @@ -1164,7 +1161,9 @@ public class AudioTrack * @param sizeInBytes the number of bytes to read in audioData after the offset. * @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION} * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if - * the parameters don't resolve to valid data and indexes. + * the parameters don't resolve to valid data and indexes, or + * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and + * needs to be recreated. */ public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) { @@ -1213,7 +1212,7 @@ public class AudioTrack public int write(short[] audioData, int offsetInShorts, int sizeInShorts) { - if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) { + if (mState == STATE_UNINITIALIZED || mAudioFormat != AudioFormat.ENCODING_PCM_16BIT) { return ERROR_INVALID_OPERATION; } @@ -1473,7 +1472,6 @@ public class AudioTrack void onPeriodicNotification(AudioTrack track); } - //--------------------------------------------------------- // Inner classes //-------------------- diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index bbe650d..7e9d279 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -22,7 +22,9 @@ import android.content.ContentUris; import android.net.Uri; import android.provider.BaseColumns; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * <p> @@ -380,6 +382,81 @@ public final class TvContract { /** The service type for radio channels that have audio only. */ public static final int SERVICE_TYPE_AUDIO = 0x2; + /** The video format for 240p. */ + public static final String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P"; + + /** The video format for 360p. */ + public static final String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P"; + + /** The video format for 480i. */ + public static final String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I"; + + /** The video format for 480p. */ + public static final String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P"; + + /** The video format for 576i. */ + public static final String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I"; + + /** The video format for 576p. */ + public static final String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P"; + + /** The video format for 720p. */ + public static final String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P"; + + /** The video format for 1080i. */ + public static final String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I"; + + /** The video format for 1080p. */ + public static final String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P"; + + /** The video format for 2160p. */ + public static final String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P"; + + /** The video format for 4320p. */ + public static final String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P"; + + /** The video resolution for standard-definition. */ + public static final String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD"; + + /** The video resolution for enhanced-definition. */ + public static final String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED"; + + /** The video resolution for high-definition. */ + public static final String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD"; + + /** The video resolution for full high-definition. */ + public static final String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD"; + + /** The video resolution for ultra high-definition. */ + public static final String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD"; + + private static final Map<String, String> VIDEO_FORMAT_TO_RESOLUTION_MAP = + new HashMap<String, String>(); + + static { + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480I, VIDEO_RESOLUTION_SD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480P, VIDEO_RESOLUTION_ED); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576I, VIDEO_RESOLUTION_SD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576P, VIDEO_RESOLUTION_ED); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_720P, VIDEO_RESOLUTION_HD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080I, VIDEO_RESOLUTION_HD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080P, VIDEO_RESOLUTION_FHD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_2160P, VIDEO_RESOLUTION_UHD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_4320P, VIDEO_RESOLUTION_UHD); + } + + /** + * Returns the video resolution (definition) for a given video format. + * + * @param videoFormat The video format defined in {@link Channels}. + * @return the corresponding video resolution string. {@code null} if the resolution string + * is not defined for the given video format. + * @see #COLUMN_VIDEO_FORMAT + */ + public static final String getVideoResolution(String videoFormat) { + return VIDEO_FORMAT_TO_RESOLUTION_MAP.get(videoFormat); + } + /** * The name of the {@link TvInputService} subclass that provides this TV channel. This * should be a fully qualified class name (such as, "com.example.project.TvInputService"). @@ -513,6 +590,24 @@ public final class TvContract { public static final String COLUMN_DESCRIPTION = "description"; /** + * The typical video format for programs from this TV channel. + * <p> + * This is primarily used to filter out channels based on video format by applications. The + * value should match one of the followings: {@link #VIDEO_FORMAT_240P}, + * {@link #VIDEO_FORMAT_360P}, {@link #VIDEO_FORMAT_480I}, {@link #VIDEO_FORMAT_480P}, + * {@link #VIDEO_FORMAT_576I}, {@link #VIDEO_FORMAT_576P}, {@link #VIDEO_FORMAT_720P}, + * {@link #VIDEO_FORMAT_1080I}, {@link #VIDEO_FORMAT_1080P}, {@link #VIDEO_FORMAT_2160P}, + * {@link #VIDEO_FORMAT_4320P}. Note that the actual video resolution of each program from a + * given channel can vary thus one should use {@link Programs#COLUMN_VIDEO_WIDTH} and + * {@link Programs#COLUMN_VIDEO_HEIGHT} to get more accurate video resolution. + * </p><p> + * Type: TEXT + * </p><p> + * @see #getVideoResolution + */ + public static final String COLUMN_VIDEO_FORMAT = "video_format"; + + /** * The flag indicating whether this TV channel is browsable or not. * <p> * A value of 1 indicates the channel is included in the channel list that applications use @@ -719,6 +814,32 @@ public final class TvContract { public static final String COLUMN_LONG_DESCRIPTION = "long_description"; /** + * The width of the video for this TV program, in the unit of pixels. + * <p> + * Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video resolution + * of the current TV program. Can be empty if it is not known initially or the program does + * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO} + * channels. + * </p><p> + * Type: INTEGER + * </p> + */ + public static final String COLUMN_VIDEO_WIDTH = "video_width"; + + /** + * The height of the video for this TV program, in the unit of pixels. + * <p> + * Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video resolution + * of the current TV program. Can be empty if it is not known initially or the program does + * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO} + * channels. + * </p><p> + * Type: INTEGER + * </p> + */ + public static final String COLUMN_VIDEO_HEIGHT = "video_height"; + + /** * The comma-separated audio languages of this TV program. * <p> * This is used to describe available audio languages included in the program. Use @@ -778,37 +899,37 @@ public final class TvContract { /** Canonical genres for TV programs. */ public static final class Genres { /** The genre for Family/Kids. */ - public static final String FAMILY_KIDS = "Family/Kids"; + public static final String FAMILY_KIDS = "FAMILY_KIDS"; /** The genre for Sports. */ - public static final String SPORTS = "Sports"; + public static final String SPORTS = "SPORTS"; /** The genre for Shopping. */ - public static final String SHOPPING = "Shopping"; + public static final String SHOPPING = "SHOPPING"; /** The genre for Movies. */ - public static final String MOVIES = "Movies"; + public static final String MOVIES = "MOVIES"; /** The genre for Comedy. */ - public static final String COMEDY = "Comedy"; + public static final String COMEDY = "COMEDY"; /** The genre for Travel. */ - public static final String TRAVEL = "Travel"; + public static final String TRAVEL = "TRAVEL"; /** The genre for Drama. */ - public static final String DRAMA = "Drama"; + public static final String DRAMA = "DRAMA"; /** The genre for Education. */ - public static final String EDUCATION = "Education"; + public static final String EDUCATION = "EDUCATION"; /** The genre for Animal/Wildlife. */ - public static final String ANIMAL_WILDLIFE = "Animal/Wildlife"; + public static final String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE"; /** The genre for News. */ - public static final String NEWS = "News"; + public static final String NEWS = "NEWS"; /** The genre for Gaming. */ - public static final String GAMING = "Gaming"; + public static final String GAMING = "GAMING"; private Genres() {} diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java index 4beb960..e5f9889 100644 --- a/media/java/android/media/tv/TvInputHardwareInfo.java +++ b/media/java/android/media/tv/TvInputHardwareInfo.java @@ -16,6 +16,7 @@ package android.media.tv; +import android.media.AudioManager; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -56,14 +57,11 @@ public final class TvInputHardwareInfo implements Parcelable { private int mDeviceId; private int mType; - // TODO: Add audio port & audio address for audio service. - // TODO: Add HDMI handle for HDMI service. + private int mAudioType; + private String mAudioAddress; + private int mHdmiPortId; - public TvInputHardwareInfo() { } - - public TvInputHardwareInfo(int deviceId, int type) { - mDeviceId = deviceId; - mType = type; + private TvInputHardwareInfo() { } public int getDeviceId() { @@ -74,6 +72,21 @@ public final class TvInputHardwareInfo implements Parcelable { return mType; } + public int getAudioType() { + return mAudioType; + } + + public String getAudioAddress() { + return mAudioAddress; + } + + public int getHdmiPortId() { + if (mType != TV_INPUT_TYPE_HDMI) { + throw new IllegalStateException(); + } + return mHdmiPortId; + } + // Parcelable @Override public int describeContents() { @@ -84,10 +97,78 @@ public final class TvInputHardwareInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mDeviceId); dest.writeInt(mType); + dest.writeInt(mAudioType); + dest.writeString(mAudioAddress); + if (mType == TV_INPUT_TYPE_HDMI) { + dest.writeInt(mHdmiPortId); + } } public void readFromParcel(Parcel source) { mDeviceId = source.readInt(); mType = source.readInt(); + mAudioType = source.readInt(); + mAudioAddress = source.readString(); + if (mType == TV_INPUT_TYPE_HDMI) { + mHdmiPortId = source.readInt(); + } + } + + public static final class Builder { + private Integer mDeviceId = null; + private Integer mType = null; + private int mAudioType = AudioManager.DEVICE_NONE; + private String mAudioAddress = ""; + private Integer mHdmiPortId = null; + + public Builder() { + } + + public Builder deviceId(int deviceId) { + mDeviceId = deviceId; + return this; + } + + public Builder type(int type) { + mType = type; + return this; + } + + public Builder audioType(int audioType) { + mAudioType = audioType; + return this; + } + + public Builder audioAddress(String audioAddress) { + mAudioAddress = audioAddress; + return this; + } + + public Builder hdmiPortId(int hdmiPortId) { + mHdmiPortId = hdmiPortId; + return this; + } + + public TvInputHardwareInfo build() { + if (mDeviceId == null || mType == null) { + throw new UnsupportedOperationException(); + } + if ((mType == TV_INPUT_TYPE_HDMI && mHdmiPortId == null) || + (mType != TV_INPUT_TYPE_HDMI && mHdmiPortId != null)) { + throw new UnsupportedOperationException(); + } + + TvInputHardwareInfo info = new TvInputHardwareInfo(); + info.mDeviceId = mDeviceId; + info.mType = mType; + info.mAudioType = mAudioType; + if (info.mAudioType != AudioManager.DEVICE_NONE) { + info.mAudioAddress = mAudioAddress; + } + if (mHdmiPortId != null) { + info.mHdmiPortId = mHdmiPortId; + } + return info; + } } } diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 2ed3d73..52db30a 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -27,6 +27,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; import android.content.res.ObbInfo; import android.content.res.ObbScanner; import android.net.Uri; @@ -157,6 +158,7 @@ public class DefaultContainerService extends IntentService { * @return Returns PackageInfoLite object containing * the package info and recommended app location. */ + @Override public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags, long threshold, String abiOverride) { PackageInfoLite ret = new PackageInfoLite(); @@ -167,14 +169,13 @@ public class DefaultContainerService extends IntentService { return ret; } - DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - PackageParser.ApkLite pkg = PackageParser.parseApkLite(packagePath, 0); - if (pkg == null) { + final File apkFile = new File(packagePath); + final PackageParser.ApkLite pkg; + try { + pkg = PackageParser.parseApkLite(apkFile, 0); + } catch (PackageParserException e) { Slog.w(TAG, "Failed to parse package"); - final File apkFile = new File(packagePath); if (!apkFile.exists()) { ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI; } else { diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 8c1a9c7..3bd8689 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -46,9 +46,7 @@ <color name="keyguard_overflow_content_color">#ff686868</color> <!-- The default recents task bar background color. --> - <color name="recents_task_bar_default_background_color">#e6444444</color> - <!-- The default recents task bar text color. --> - <color name="recents_task_bar_default_text_color">#ffeeeeee</color> + <color name="recents_task_bar_default_background_color">#ffe6e6e6</color> <!-- The recents task bar light text color to be drawn on top of dark backgrounds. --> <color name="recents_task_bar_light_text_color">#ffeeeeee</color> <!-- The recents task bar dark text color to be drawn on top of light backgrounds. --> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 88ff726..9ea346b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -277,13 +277,21 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) { // If we have a focused task, then launch that task if (!mRecentsView.launchFocusedTask()) { - // If there are any tasks, then launch the first task - if (!mRecentsView.launchFirstTask()) { - // We really shouldn't hit this, but if we do, just animate out (aka. finish) + if (mConfig.launchedFromHome) { + // Just start the animation out of recents ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, null, mFinishRunnable, null); mRecentsView.startExitToHomeAnimation( new ViewAnimation.TaskViewExitContext(exitTrigger)); + } else { + // Otherwise, try and launch the first task + if (!mRecentsView.launchFirstTask()) { + // If there are no tasks, then just finish recents + ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, + null, mFinishRunnable, null); + mRecentsView.startExitToHomeAnimation( + new ViewAnimation.TaskViewExitContext(exitTrigger)); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 63ef773..10978ca 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -81,7 +81,6 @@ public class RecentsConfiguration { /** Task bar colors */ public int taskBarViewDefaultBackgroundColor; - public int taskBarViewDefaultTextColor; public int taskBarViewLightTextColor; public int taskBarViewDarkTextColor; public int taskBarViewHighlightColor; @@ -202,8 +201,6 @@ public class RecentsConfiguration { // Task bar colors taskBarViewDefaultBackgroundColor = res.getColor(R.color.recents_task_bar_default_background_color); - taskBarViewDefaultTextColor = - res.getColor(R.color.recents_task_bar_default_text_color); taskBarViewLightTextColor = res.getColor(R.color.recents_task_bar_light_text_color); taskBarViewDarkTextColor = diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java index bf25760..1ef58ad 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java @@ -151,16 +151,14 @@ class TaskBarView extends FrameLayout { mActivityDescription.setText(t.activityLabel); // Try and apply the system ui tint int tint = t.colorPrimary; - if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) { - setBackgroundColor(tint); - mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint, - mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor)); - mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint, - mLightDismissDrawable, mDarkDismissDrawable)); - } else { - setBackgroundColor(mConfig.taskBarViewDefaultBackgroundColor); - mActivityDescription.setTextColor(mConfig.taskBarViewDefaultTextColor); + if (!Constants.DebugFlags.App.EnableTaskBarThemeColors || tint == 0) { + tint = mConfig.taskBarViewDefaultBackgroundColor; } + setBackgroundColor(tint); + mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint, + mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor)); + mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint, + mLightDismissDrawable, mDarkDismissDrawable)); } /** Unbinds the bar view from the task */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 6c12218..55f9335 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -973,7 +973,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int getExitTransformsForFilterAnimation(ArrayList<Task> curTasks, ArrayList<TaskViewTransform> curTaskTransforms, ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms, - HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut, + HashMap<TaskView, TaskViewTransform> childViewTransformsOut, ArrayList<TaskView> childrenToRemoveOut) { // Animate all of the existing views out of view (if they are not in the visible range in // the new stack) or to their final positions in the new stack @@ -1003,9 +1003,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal (int) tv.getTranslationY())); } - int startDelay = offset * - Constants.Values.TaskStackView.FilterStartDelay; - childViewTransformsOut.put(tv, new Pair(startDelay, toTransform)); + toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay; + childViewTransformsOut.put(tv, toTransform); offset++; } return mConfig.filteringCurrentViewsAnimDuration; @@ -1017,7 +1016,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms, - HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut) { + HashMap<TaskView, TaskViewTransform> childViewTransformsOut) { int offset = 0; int movement = 0; int taskCount = tasks.size(); @@ -1035,9 +1034,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal tv.prepareTaskTransformForFilterTaskHidden(fromTransform); tv.updateViewPropertiesToTaskTransform(fromTransform, 0); - int startDelay = offset * - Constants.Values.TaskStackView.FilterStartDelay; - childViewTransformsOut.put(tv, new Pair(startDelay, toTransform)); + toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay; + childViewTransformsOut.put(tv, toTransform); // Use the movement of the new views to calculate the duration of the animation movement = Math.max(movement, @@ -1057,8 +1055,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Calculate the transforms to animate out all the existing views if they are not in the // new visible range (or to their final positions in the stack if they are) final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>(); - final HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransforms = - new HashMap<TaskView, Pair<Integer, TaskViewTransform>>(); + final HashMap<TaskView, TaskViewTransform> childViewTransforms = + new HashMap<TaskView, TaskViewTransform>(); int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks, taskTransforms, childViewTransforms, childrenToRemove); @@ -1073,10 +1071,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Animate all the views to their final transforms for (final TaskView tv : childViewTransforms.keySet()) { - Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv); + TaskViewTransform t = childViewTransforms.get(tv); tv.animate().cancel(); tv.animate() - .setStartDelay(t.first) .withEndAction(new Runnable() { @Override public void run() { @@ -1093,15 +1090,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int duration = getEnterTransformsForFilterAnimation(tasks, taskTransforms, childViewTransforms); for (final TaskView tv : childViewTransforms.keySet()) { - Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv); - tv.animate().setStartDelay(t.first); - tv.updateViewPropertiesToTaskTransform(t.second, duration); + TaskViewTransform t = childViewTransforms.get(tv); + tv.updateViewPropertiesToTaskTransform(t, duration); } } } } }); - tv.updateViewPropertiesToTaskTransform(t.second, duration); + tv.updateViewPropertiesToTaskTransform(t, duration); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 0b19162..cfba74c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -202,7 +202,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On if (useLayers) { anim.withLayer(); } - anim.setStartDelay(0) + anim.setStartDelay(toTransform.startDelay) .setDuration(duration) .setInterpolator(mConfig.fastOutSlowInInterpolator) .start(); @@ -248,6 +248,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On // Fade the view out and slide it away toTransform.alpha = 0f; toTransform.translationY += 200; + toTransform.translationZ = 0; } /** @@ -585,19 +586,25 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On } @Override - public void onClick(View v) { - if (v == mBarView.mApplicationIcon) { - mCb.onTaskIconClicked(this); - } else if (v == mBarView.mDismissButton) { - // Animate out the view and call the callback - final TaskView tv = this; - startDeleteTaskAnimation(new Runnable() { - @Override - public void run() { - mCb.onTaskDismissed(tv); + public void onClick(final View v) { + // We purposely post the handler delayed to allow for the touch feedback to draw + final TaskView tv = this; + postDelayed(new Runnable() { + @Override + public void run() { + if (v == mBarView.mApplicationIcon) { + mCb.onTaskIconClicked(tv); + } else if (v == mBarView.mDismissButton) { + // Animate out the view and call the callback + startDeleteTaskAnimation(new Runnable() { + @Override + public void run() { + mCb.onTaskDismissed(tv); + } + }); } - }); - } + } + }, 125); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java index 1947e30..b351b03 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java @@ -21,6 +21,7 @@ import android.graphics.Rect; /* The transform state for a task view */ public class TaskViewTransform { + public int startDelay = 0; public int translationY = 0; public int translationZ = 0; public float scale = 1f; @@ -35,6 +36,7 @@ public class TaskViewTransform { } public TaskViewTransform(TaskViewTransform o) { + startDelay = o.startDelay; translationY = o.translationY; translationZ = o.translationZ; scale = o.scale; @@ -47,6 +49,7 @@ public class TaskViewTransform { /** Resets the current transform */ public void reset() { + startDelay = 0; translationY = 0; translationZ = 0; scale = 1f; @@ -76,8 +79,8 @@ public class TaskViewTransform { @Override public String toString() { - return "TaskViewTransform y: " + translationY + " z: " + translationZ + " scale: " + scale + - " alpha: " + alpha + " visible: " + visible + " rect: " + rect + + return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ + + " scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha; } } diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java index 2191b54..d9588e8 100644 --- a/rs/java/android/renderscript/Allocation.java +++ b/rs/java/android/renderscript/Allocation.java @@ -770,10 +770,11 @@ public class Allocation extends BaseObj { mRS.validate(); int eSize = mType.mElement.getBytesSize(); final byte[] data = fp.getData(); + int data_length = fp.getPos(); - int count = data.length / eSize; - if ((eSize * count) != data.length) { - throw new RSIllegalArgumentException("Field packer length " + data.length + + int count = data_length / eSize; + if ((eSize * count) != data_length) { + throw new RSIllegalArgumentException("Field packer length " + data_length + " not divisible by element size " + eSize + "."); } copy1DRangeFromUnchecked(xoff, count, data); @@ -797,16 +798,17 @@ public class Allocation extends BaseObj { } final byte[] data = fp.getData(); + int data_length = fp.getPos(); int eSize = mType.mElement.mElements[component_number].getBytesSize(); eSize *= mType.mElement.mArraySizes[component_number]; - if (data.length != eSize) { - throw new RSIllegalArgumentException("Field packer sizelength " + data.length + + if (data_length != eSize) { + throw new RSIllegalArgumentException("Field packer sizelength " + data_length + " does not match component size " + eSize + "."); } mRS.nAllocationElementData1D(getIDSafe(), xoff, mSelectedLOD, - component_number, data, data.length); + component_number, data, data_length); } private void data1DChecks(int off, int count, int len, int dataSize) { diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java index 55b671d..c6b5b0d 100644 --- a/rs/java/android/renderscript/Element.java +++ b/rs/java/android/renderscript/Element.java @@ -140,17 +140,17 @@ public class Element extends BaseObj { MATRIX_3X3 (17, 36), MATRIX_2X2 (18, 16), - RS_ELEMENT (1000, 4), - RS_TYPE (1001, 4), - RS_ALLOCATION (1002, 4), - RS_SAMPLER (1003, 4), - RS_SCRIPT (1004, 4), - RS_MESH (1005, 4), - RS_PROGRAM_FRAGMENT (1006, 4), - RS_PROGRAM_VERTEX (1007, 4), - RS_PROGRAM_RASTER (1008, 4), - RS_PROGRAM_STORE (1009, 4), - RS_FONT (1010, 4); + RS_ELEMENT (1000), + RS_TYPE (1001), + RS_ALLOCATION (1002), + RS_SAMPLER (1003), + RS_SCRIPT (1004), + RS_MESH (1005), + RS_PROGRAM_FRAGMENT (1006), + RS_PROGRAM_VERTEX (1007), + RS_PROGRAM_RASTER (1008), + RS_PROGRAM_STORE (1009), + RS_FONT (1010); int mID; int mSize; @@ -158,6 +158,14 @@ public class Element extends BaseObj { mID = id; mSize = size; } + + DataType(int id) { + mID = id; + mSize = 4; + if (RenderScript.sPointerSize == 8) { + mSize = 32; + } + } } /** diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java index c9bba69..f39aa5f 100644 --- a/rs/java/android/renderscript/FieldPacker.java +++ b/rs/java/android/renderscript/FieldPacker.java @@ -75,7 +75,7 @@ public class FieldPacker { mPos = 0; } public void reset(int i) { - if ((i < 0) || (i >= mLen)) { + if ((i < 0) || (i > mLen)) { throw new RSIllegalArgumentException("out of range argument: " + i); } mPos = i; @@ -605,6 +605,15 @@ public class FieldPacker { return mData; } + /** + * Get the actual length used for the FieldPacker. + * + * @hide + */ + public int getPos() { + return mPos; + } + private final byte mData[]; private int mPos; private int mLen; diff --git a/rs/java/android/renderscript/ProgramVertexFixedFunction.java b/rs/java/android/renderscript/ProgramVertexFixedFunction.java index 5173af2..45840ae 100644 --- a/rs/java/android/renderscript/ProgramVertexFixedFunction.java +++ b/rs/java/android/renderscript/ProgramVertexFixedFunction.java @@ -245,6 +245,9 @@ public class ProgramVertexFixedFunction extends ProgramVertex { for(int i = 0; i < 16; i ++) { mIOBuffer.addF32(m.mMat[i]); } + // Reset the buffer back to the end, since we want to flush all of + // the contents back (and not just what we wrote now). + mIOBuffer.reset(mIOBuffer.getData().length); mAlloc.setFromFieldPacker(0, mIOBuffer); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 9e893da..9f080ca 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -219,6 +219,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo // Wait for a down key event to start processing. if (!mKeyEventSequenceStarted) { if (event.getAction() != KeyEvent.ACTION_DOWN) { + super.onInputEvent(event, policyFlags); return; } mKeyEventSequenceStarted = true; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 9b1d0e3..9264186 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -46,6 +46,8 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.IActivityManager.WaitResult; import android.app.ResultInfo; import android.app.StatusBarManager; +import android.app.admin.DevicePolicyManager; +import android.app.admin.IDevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.IIntentSender; @@ -143,6 +145,7 @@ public final class ActivityStackSupervisor implements DisplayListener { /** Status Bar Service **/ private IBinder mToken = new Binder(); private IStatusBarService mStatusBarService; + private IDevicePolicyManager mDevicePolicyManager; // For debugging to make sure the caller when acquiring/releasing our // wake lock is the system process. @@ -285,6 +288,19 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + private IDevicePolicyManager getDevicePolicyManager() { + synchronized (mService) { + if (mDevicePolicyManager == null) { + mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface( + ServiceManager.checkService(Context.DEVICE_POLICY_SERVICE)); + if (mDevicePolicyManager == null) { + Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE"); + } + } + return mDevicePolicyManager; + } + } + void setWindowManager(WindowManagerService wm) { synchronized (mService) { mWindowManager = wm; @@ -2989,12 +3005,15 @@ public final class ActivityStackSupervisor implements DisplayListener { } void setLockTaskModeLocked(TaskRecord task) { - final Message lockTaskMsg = Message.obtain(); if (task == null) { - // Take out of lock task mode. - mLockTaskModeTask = null; - lockTaskMsg.what = LOCK_TASK_END_MSG; - mHandler.sendMessage(lockTaskMsg); + // Take out of lock task mode if necessary + if (mLockTaskModeTask != null) { + final Message lockTaskMsg = Message.obtain(); + lockTaskMsg.arg1 = mLockTaskModeTask.userId; + lockTaskMsg.what = LOCK_TASK_END_MSG; + mLockTaskModeTask = null; + mHandler.sendMessage(lockTaskMsg); + } return; } if (isLockTaskModeViolation(task)) { @@ -3004,6 +3023,10 @@ public final class ActivityStackSupervisor implements DisplayListener { mLockTaskModeTask = task; findTaskToMoveToFrontLocked(task, 0, null); resumeTopActivitiesLocked(); + + final Message lockTaskMsg = Message.obtain(); + lockTaskMsg.obj = mLockTaskModeTask.intent.getComponent().getPackageName(); + lockTaskMsg.arg1 = mLockTaskModeTask.userId; lockTaskMsg.what = LOCK_TASK_START_MSG; mHandler.sendMessage(lockTaskMsg); } @@ -3112,6 +3135,11 @@ public final class ActivityStackSupervisor implements DisplayListener { (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK, mToken, mService.mContext.getPackageName()); } + if (getDevicePolicyManager() != null) { + getDevicePolicyManager().notifyLockTaskModeChanged(true, + (String)msg.obj, + msg.arg1); + } } catch (RemoteException ex) { throw new RuntimeException(ex); } @@ -3124,6 +3152,10 @@ public final class ActivityStackSupervisor implements DisplayListener { (StatusBarManager.DISABLE_NONE, mToken, mService.mContext.getPackageName()); } + if (getDevicePolicyManager() != null) { + getDevicePolicyManager().notifyLockTaskModeChanged(false, null, + msg.arg1); + } } catch (RemoteException ex) { throw new RuntimeException(ex); } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 0e9a9cc..cab2728 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import android.app.AppGlobals; import android.app.job.JobInfo; import android.app.job.JobScheduler; import android.app.job.JobService; @@ -31,8 +32,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.os.Binder; import android.os.Handler; @@ -616,10 +617,13 @@ public class JobSchedulerService extends com.android.server.SystemService // job that runs one of the app's services, as well as verifying that the // named service properly requires the BIND_JOB_SERVICE permission private void enforceValidJobRequest(int uid, JobInfo job) { - final PackageManager pm = getContext().getPackageManager(); + final IPackageManager pm = AppGlobals.getPackageManager(); final ComponentName service = job.getService(); try { - ServiceInfo si = pm.getServiceInfo(service, 0); + ServiceInfo si = pm.getServiceInfo(service, 0, UserHandle.getUserId(uid)); + if (si == null) { + throw new IllegalArgumentException("No such service " + service); + } if (si.applicationInfo.uid != uid) { throw new IllegalArgumentException("uid " + uid + " cannot schedule job in " + service.getPackageName()); @@ -628,8 +632,8 @@ public class JobSchedulerService extends com.android.server.SystemService throw new IllegalArgumentException("Scheduled service " + service + " does not require android.permission.BIND_JOB_SERVICE permission"); } - } catch (NameNotFoundException e) { - throw new IllegalArgumentException("No such service: " + service); + } catch (RemoteException e) { + // Can't happen; the Package Manager is in this same process } } diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java index 3d432dc..3ce19c1 100644 --- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java +++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java @@ -32,36 +32,32 @@ import android.os.UserHandle; */ class CrossProfileIntentFilter extends IntentFilter { private static final String ATTR_TARGET_USER_ID = "targetUserId"; - private static final String ATTR_USER_ID_DEST = "userIdDest";//Old name. Kept for compatibility. - private static final String ATTR_REMOVABLE = "removable"; + private static final String ATTR_FLAGS = "flags"; private static final String ATTR_FILTER = "filter"; private static final String TAG = "CrossProfileIntentFilter"; // If the intent matches the IntentFilter, then it can be forwarded to this userId. final int mTargetUserId; - boolean mRemovable; + final int mFlags; - CrossProfileIntentFilter(IntentFilter filter, boolean removable, int targetUserId) { + CrossProfileIntentFilter(IntentFilter filter, int targetUserId, int flags) { super(filter); mTargetUserId = targetUserId; - mRemovable = removable; + mFlags = flags; } public int getTargetUserId() { return mTargetUserId; } - public boolean isRemovable() { - return mRemovable; + public int getFlags() { + return mFlags; } CrossProfileIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException { String targetUserIdString = parser.getAttributeValue(null, ATTR_TARGET_USER_ID); if (targetUserIdString == null) { - targetUserIdString = parser.getAttributeValue(null, ATTR_USER_ID_DEST); - } - if (targetUserIdString == null) { String msg = "Missing element under " + TAG +": " + ATTR_TARGET_USER_ID + " at " + parser.getPositionDescription(); PackageManagerService.reportSettingsProblem(Log.WARN, msg); @@ -69,9 +65,14 @@ class CrossProfileIntentFilter extends IntentFilter { } else { mTargetUserId = Integer.parseInt(targetUserIdString); } - String removableString = parser.getAttributeValue(null, ATTR_REMOVABLE); - if (removableString != null) { - mRemovable = Boolean.parseBoolean(removableString); + String flagsString = parser.getAttributeValue(null, ATTR_FLAGS); + if (flagsString == null) { + String msg = "Missing element under " + TAG +": " + ATTR_FLAGS + " at " + + parser.getPositionDescription(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + mFlags = 0; + } else { + mFlags = Integer.parseInt(flagsString); } int outerDepth = parser.getDepth(); String tagName = parser.getName(); @@ -104,7 +105,7 @@ class CrossProfileIntentFilter extends IntentFilter { public void writeToXml(XmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_TARGET_USER_ID, Integer.toString(mTargetUserId)); - serializer.attribute(null, ATTR_REMOVABLE, Boolean.toString(mRemovable)); + serializer.attribute(null, ATTR_FLAGS, Integer.toString(mFlags)); serializer.startTag(null, ATTR_FILTER); super.writeToXml(serializer); serializer.endTag(null, ATTR_FILTER); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index dd33771..89ab2ae 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -29,6 +29,7 @@ import android.content.pm.PackageInstallerParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ApkLite; +import android.content.pm.PackageParser.PackageParserException; import android.content.pm.Signature; import android.os.Build; import android.os.Bundle; @@ -50,14 +51,10 @@ import com.android.internal.content.NativeLibraryHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; -import libcore.io.IoUtils; import libcore.io.Libcore; -import libcore.io.Streams; import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -297,11 +294,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Verify that all staged packages are internally consistent for (File file : files) { - final ApkLite info = PackageParser.parseApkLite(file.getAbsolutePath(), - PackageParser.PARSE_GET_SIGNATURES); - if (info == null) { + final ApkLite info; + try { + info = PackageParser.parseApkLite(file, PackageParser.PARSE_GET_SIGNATURES); + } catch (PackageParserException e) { throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, - "Failed to parse " + file); + "Failed to parse " + file + ": " + e); } if (!seenSplits.add(info.splitName)) { @@ -356,11 +354,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Missing existing base package for " + mPackageName); } - final ApkLite info = PackageParser.parseApkLite(app.sourceDir, - PackageParser.PARSE_GET_SIGNATURES); - if (info == null) { + final ApkLite info; + try { + info = PackageParser.parseApkLite(new File(app.sourceDir), + PackageParser.PARSE_GET_SIGNATURES); + } catch (PackageParserException e) { throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, - "Failed to parse existing base " + app.sourceDir); + "Failed to parse existing base " + app.sourceDir + ": " + e); } assertPackageConsistent("Existing base", info.packageName, info.versionCode, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index dc76455..a7fc7eb 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -147,6 +147,7 @@ import android.util.LogPrinter; import android.util.PrintStreamPrinter; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.Xml; import android.view.Display; @@ -3347,7 +3348,7 @@ public class PackageManagerService extends IPackageManager.Stub { } /* - * Returns if intent can be forwarded from the userId from to dest + * Returns if intent can be forwarded from the sourceUserId to the targetUserId */ @Override public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId, @@ -3367,9 +3368,9 @@ public class PackageManagerService extends IPackageManager.Stub { private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { - CrossProfileIntentResolver cpir = mSettings.mCrossProfileIntentResolvers.get(userId); - if (cpir != null) { - return cpir.queryIntent(intent, resolvedType, false, userId); + CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId); + if (resolver != null) { + return resolver.queryIntent(intent, resolvedType, false, userId); } return null; } @@ -3402,36 +3403,24 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final String pkgName = intent.getPackage(); if (pkgName == null) { - List<ResolveInfo> result = - mActivities.queryIntent(intent, resolvedType, flags, userId); - // Checking if we can forward the intent to another user - List<CrossProfileIntentFilter> cpifs = + List<ResolveInfo> result; + List<CrossProfileIntentFilter> matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); - if (cpifs != null) { - CrossProfileIntentFilter crossProfileIntentFilterWithResult = null; - HashSet<Integer> alreadyTriedUserIds = new HashSet<Integer>(); - for (CrossProfileIntentFilter cpif : cpifs) { - int targetUserId = cpif.getTargetUserId(); - // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and - // match the same an intent. For performance reasons, it is better not to - // run queryIntent twice for the same userId - if (!alreadyTriedUserIds.contains(targetUserId)) { - List<ResolveInfo> resultUser = mActivities.queryIntent(intent, - resolvedType, flags, targetUserId); - if (resultUser != null) { - crossProfileIntentFilterWithResult = cpif; - // As soon as there is a match in another user, we add the - // intentForwarderActivity to the list of ResolveInfo. - break; - } - alreadyTriedUserIds.add(targetUserId); - } - } - if (crossProfileIntentFilterWithResult != null) { - ResolveInfo forwardingResolveInfo = createForwardingResolveInfo( - crossProfileIntentFilterWithResult, userId); - result.add(forwardingResolveInfo); - } + // Check for results that need to skip the current profile. + ResolveInfo resolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent, + resolvedType, flags, userId); + if (resolveInfo != null) { + result = new ArrayList<ResolveInfo>(1); + result.add(resolveInfo); + return result; + } + // Check for results in the current profile. + result = mActivities.queryIntent(intent, resolvedType, flags, userId); + // Check for cross profile results. + resolveInfo = queryCrossProfileIntents( + matchingFilters, intent, resolvedType, flags, userId); + if (resolveInfo != null) { + result.add(resolveInfo); } return result; } @@ -3444,10 +3433,68 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter cpif, + private ResolveInfo querySkipCurrentProfileIntents( + List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType, + int flags, int sourceUserId) { + if (matchingFilters != null) { + int size = matchingFilters.size(); + for (int i = 0; i < size; i ++) { + CrossProfileIntentFilter filter = matchingFilters.get(i); + if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) { + // Checking if there are activities in the target user that can handle the + // intent. + ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType, + flags, sourceUserId); + if (resolveInfo != null) { + return createForwardingResolveInfo(filter, sourceUserId); + } + } + } + } + return null; + } + + // Return matching ResolveInfo if any for skip current profile intent filters. + private ResolveInfo queryCrossProfileIntents( + List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType, + int flags, int sourceUserId) { + if (matchingFilters != null) { + // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and + // match the same intent. For performance reasons, it is better not to + // run queryIntent twice for the same userId + SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray(); + int size = matchingFilters.size(); + for (int i = 0; i < size; i++) { + CrossProfileIntentFilter filter = matchingFilters.get(i); + int targetUserId = filter.getTargetUserId(); + if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) == 0 + && !alreadyTriedUserIds.get(targetUserId)) { + // Checking if there are activities in the target user that can handle the + // intent. + ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType, + flags, sourceUserId); + if (resolveInfo != null) return resolveInfo; + alreadyTriedUserIds.put(targetUserId, true); + } + } + } + return null; + } + + private ResolveInfo checkTargetCanHandle(CrossProfileIntentFilter filter, Intent intent, + String resolvedType, int flags, int sourceUserId) { + List<ResolveInfo> resultTargetUser = mActivities.queryIntent(intent, + resolvedType, flags, filter.getTargetUserId()); + if (resultTargetUser != null) { + return createForwardingResolveInfo(filter, sourceUserId); + } + return null; + } + + private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, int sourceUserId) { String className; - int targetUserId = cpif.getTargetUserId(); + int targetUserId = filter.getTargetUserId(); if (targetUserId == UserHandle.USER_OWNER) { className = FORWARD_INTENT_TO_USER_OWNER; } else { @@ -3463,7 +3510,7 @@ public class PackageManagerService extends IPackageManager.Stub { forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; - forwardingResolveInfo.filter = cpif; + forwardingResolveInfo.filter = filter; return forwardingResolveInfo; } @@ -4199,14 +4246,18 @@ public class PackageManagerService extends IPackageManager.Stub { String scanPath = scanFile.getPath(); if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath); parseFlags |= mDefParseFlags; - PackageParser pp = new PackageParser(scanPath); + PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); + pp.setDisplayMetrics(mMetrics); + + if ((scanMode & SCAN_TRUSTED_OVERLAY) != 0) { + parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY; + } final PackageParser.Package pkg; try { - pkg = pp.parseMonolithicPackage(scanFile, mMetrics, parseFlags, - (scanMode & SCAN_TRUSTED_OVERLAY) != 0); + pkg = pp.parseMonolithicPackage(scanFile, parseFlags); } catch (PackageParserException e) { mLastScanError = e.error; return null; @@ -4637,12 +4688,7 @@ public class PackageManagerService extends IPackageManager.Stub { } if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { - final ArrayList<String> paths = new ArrayList<>(); - paths.add(pkg.codePath); - if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { - Collections.addAll(paths, pkg.splitCodePaths); - } - + final Collection<String> paths = pkg.getAllCodePaths(); for (String path : paths) { try { boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path, @@ -4832,10 +4878,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } if (p != null) { - usesLibraryFiles.add(p.codePath); - if (!ArrayUtils.isEmpty(p.splitCodePaths)) { - Collections.addAll(usesLibraryFiles, p.splitCodePaths); - } + usesLibraryFiles.addAll(p.getAllCodePaths()); } } @@ -5674,7 +5717,8 @@ public class PackageManagerService extends IPackageManager.Stub { try { ksm.addSigningKeySetToPackage(pkg.packageName, pkg.mSigningKeys); if (pkg.mKeySetMapping != null) { - for (Map.Entry<String, Set<PublicKey>> entry : pkg.mKeySetMapping.entrySet()) { + for (Map.Entry<String, ArraySet<PublicKey>> entry : + pkg.mKeySetMapping.entrySet()) { if (entry.getValue() != null) { ksm.addDefinedKeySetToPackage(pkg.packageName, entry.getValue(), entry.getKey()); @@ -9713,7 +9757,7 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.INSTALL_SUCCEEDED; } - }; + } static String getAsecPackageName(String packageCid) { int idx = packageCid.lastIndexOf("-"); @@ -10206,13 +10250,13 @@ public class PackageManagerService extends IPackageManager.Stub { int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | (onSd ? PackageParser.PARSE_ON_SDCARD : 0); - PackageParser pp = new PackageParser(tmpPackageFile.getPath()); + PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); + pp.setDisplayMetrics(mMetrics); final PackageParser.Package pkg; try { - pkg = pp.parseMonolithicPackage(tmpPackageFile, mMetrics, - parseFlags); + pkg = pp.parseMonolithicPackage(tmpPackageFile, parseFlags); } catch (PackageParserException e) { res.returnCode = e.error; return; @@ -11542,17 +11586,18 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId) { + public void addCrossProfileIntentFilter(IntentFilter intentFilter, int sourceUserId, + int targetUserId, int flags) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - if (filter.countActions() == 0) { + if (intentFilter.countActions() == 0) { Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions"); return; } synchronized (mPackages) { - mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter( - new CrossProfileIntentFilter(filter, removable, targetUserId)); + CrossProfileIntentFilter filter = new CrossProfileIntentFilter(intentFilter, + targetUserId, flags); + mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter(filter); mSettings.writePackageRestrictionsLPr(sourceUserId); } } @@ -11562,12 +11607,14 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); synchronized (mPackages) { - CrossProfileIntentResolver cpir = + CrossProfileIntentResolver resolver = mSettings.editCrossProfileIntentResolverLPw(sourceUserId); HashSet<CrossProfileIntentFilter> set = - new HashSet<CrossProfileIntentFilter>(cpir.filterSet()); - for (CrossProfileIntentFilter cpif : set) { - if (cpif.isRemovable()) cpir.removeFilter(cpif); + new HashSet<CrossProfileIntentFilter>(resolver.filterSet()); + for (CrossProfileIntentFilter filter : set) { + if ((filter.getFlags() & PackageManager.SET_BY_PROFILE_OWNER) != 0) { + resolver.removeFilter(filter); + } } mSettings.writePackageRestrictionsLPr(sourceUserId); } diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java index 34168a8..1535e7a 100644 --- a/services/core/java/com/android/server/tv/TvInputHal.java +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -94,8 +94,7 @@ final class TvInputHal { } // Called from native - private void deviceAvailableFromNative(int deviceId, int type) { - final TvInputHardwareInfo info = new TvInputHardwareInfo(deviceId, type); + private void deviceAvailableFromNative(final TvInputHardwareInfo info) { mHandler.post(new Runnable() { @Override public void run() { @@ -105,23 +104,21 @@ final class TvInputHal { }); } - private void deviceUnavailableFromNative(int deviceId) { - final int id = deviceId; + private void deviceUnavailableFromNative(final int deviceId) { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onDeviceUnavailable(id); + mCallback.onDeviceUnavailable(deviceId); } }); } - private void streamConfigsChangedFromNative(int deviceId) { - final int id = deviceId; + private void streamConfigsChangedFromNative(final int deviceId) { mHandler.post(new Runnable() { @Override public void run() { - retrieveStreamConfigs(id); - mCallback.onStreamConfigurationChanged(id, mStreamConfigs); + retrieveStreamConfigs(deviceId); + mCallback.onStreamConfigurationChanged(deviceId, mStreamConfigs); } }); } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index d72ed9e..1146f0f 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -17,6 +17,11 @@ package com.android.server.tv; import android.content.Context; +import android.media.AudioDevicePort; +import android.media.AudioManager; +import android.media.AudioPatch; +import android.media.AudioPort; +import android.media.AudioPortConfig; import android.media.tv.ITvInputHardware; import android.media.tv.ITvInputHardwareCallback; import android.media.tv.TvInputHardwareInfo; @@ -48,11 +53,13 @@ class TvInputHardwareManager implements TvInputHal.Callback { private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>(); private final Context mContext; private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>(); + private final AudioManager mAudioManager; private final Object mLock = new Object(); public TvInputHardwareManager(Context context) { mContext = context; + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // TODO(hdmi): mHdmiManager = mContext.getSystemService(...); // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient(); mHal.init(); @@ -258,12 +265,48 @@ class TvInputHardwareManager implements TvInputHal.Callback { private boolean mReleased = false; private final Object mImplLock = new Object(); + private final AudioDevicePort mAudioSource; + private final AudioDevicePort mAudioSink; + private AudioPatch mAudioPatch = null; + public TvInputHardwareImpl(TvInputHardwareInfo info) { mInfo = info; + AudioDevicePort audioSource = null; + AudioDevicePort audioSink = null; + if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) { + ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>(); + if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) { + // Find source + for (AudioPort port : devicePorts) { + AudioDevicePort devicePort = (AudioDevicePort) port; + if (devicePort.type() == mInfo.getAudioType() && + devicePort.address().equals(mInfo.getAudioAddress())) { + audioSource = devicePort; + break; + } + } + // Find sink + // TODO: App may want to specify sink device? + int sinkDevices = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); + for (AudioPort port : devicePorts) { + AudioDevicePort devicePort = (AudioDevicePort) port; + if (devicePort.type() == sinkDevices) { + audioSink = devicePort; + break; + } + } + } + } + mAudioSource = audioSource; + mAudioSink = audioSink; } public void release() { synchronized (mImplLock) { + if (mAudioPatch != null) { + mAudioManager.releaseAudioPatch(mAudioPatch); + mAudioPatch = null; + } mReleased = true; } } @@ -288,6 +331,22 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } } + if (mAudioSource != null && mAudioSink != null) { + if (surface != null) { + AudioPortConfig sourceConfig = mAudioSource.activeConfig(); + AudioPortConfig sinkConfig = mAudioSink.activeConfig(); + AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch }; + // TODO: build config if activeConfig() == null + mAudioManager.createAudioPatch( + audioPatchArray, + new AudioPortConfig[] { sourceConfig }, + new AudioPortConfig[] { sinkConfig }); + mAudioPatch = audioPatchArray[0]; + } else { + mAudioManager.releaseAudioPatch(mAudioPatch); + mAudioPatch = null; + } + } return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS; } } @@ -299,7 +358,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { throw new IllegalStateException("Device already released."); } } - // TODO + // TODO: Use AudioGain? } @Override diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index afe629d..9cecdf0 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -54,6 +54,18 @@ static struct { jmethodID build; } gTvStreamConfigBuilderClassInfo; +static struct { + jclass clazz; + + jmethodID constructor; + jmethodID deviceId; + jmethodID type; + jmethodID hdmiPortId; + jmethodID audioType; + jmethodID audioAddress; + jmethodID build; +} gTvInputHardwareInfoBuilderClassInfo; + //////////////////////////////////////////////////////////////////////////////// class JTvInputHal { @@ -209,7 +221,6 @@ const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numCo return configs; } - // static void JTvInputHal::notify( tv_input_device_t* dev, tv_input_event_t* event, void* data) { @@ -232,11 +243,36 @@ void JTvInputHal::notify( void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) { JNIEnv* env = AndroidRuntime::getJNIEnv(); mConnections.add(info.device_id, Connection()); + + jobject builder = env->NewObject( + gTvInputHardwareInfoBuilderClassInfo.clazz, + gTvInputHardwareInfoBuilderClassInfo.constructor); + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id); + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type); + if (info.type == TV_INPUT_TYPE_HDMI) { + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id); + } + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type); + if (info.audio_type != AUDIO_DEVICE_NONE) { + jstring audioAddress = env->NewStringUTF(info.audio_address); + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress); + env->DeleteLocalRef(audioAddress); + } + + jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build); + env->CallVoidMethod( mThiz, gTvInputHalClassInfo.deviceAvailable, - info.device_id, - info.type); + infoObject); + + env->DeleteLocalRef(builder); + env->DeleteLocalRef(infoObject); } void JTvInputHal::onDeviceUnavailable(int deviceId) { @@ -339,7 +375,8 @@ int register_android_server_tv_TvInputHal(JNIEnv* env) { FIND_CLASS(clazz, "com/android/server/tv/TvInputHal"); GET_METHOD_ID( - gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V"); + gTvInputHalClassInfo.deviceAvailable, clazz, + "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V"); GET_METHOD_ID( gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V"); GET_METHOD_ID( @@ -382,6 +419,40 @@ int register_android_server_tv_TvInputHal(JNIEnv* env) { gTvStreamConfigBuilderClassInfo.clazz, "build", "()Landroid/media/tv/TvStreamConfig;"); + FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz, + "android/media/tv/TvInputHardwareInfo$Builder"); + gTvInputHardwareInfoBuilderClassInfo.clazz = + jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz)); + + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.constructor, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "<init>", "()V"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.deviceId, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.type, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.audioType, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.audioAddress, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.build, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "build", "()Landroid/media/tv/TvInputHardwareInfo;"); + return 0; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 765a33d..2801f4f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1322,7 +1322,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void manageMonitoringCertificateNotification(Intent intent) { final NotificationManager notificationManager = getNotificationManager(); - final boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); + final boolean hasCert = !(new TrustedCertificateStore().userAliases().isEmpty()); if (! hasCert) { if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) { for (UserInfo user : mUserManager.getUsers()) { @@ -2382,13 +2382,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return !"".equals(state); } - public boolean installCaCert(byte[] certBuffer) throws RemoteException { - mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); - KeyChainConnection keyChainConnection = null; + public boolean installCaCert(ComponentName who, byte[] certBuffer) throws RemoteException { + if (who == null) { + mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); + } else { + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + } + } + byte[] pemCert; try { X509Certificate cert = parseCert(certBuffer); - pemCert = Credentials.convertToPem(cert); + pemCert = Credentials.convertToPem(cert); } catch (CertificateException ce) { Log.e(LOG_TAG, "Problem converting cert", ce); return false; @@ -2396,20 +2402,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Log.e(LOG_TAG, "Problem reading cert", ioe); return false; } + + final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); + final long id = Binder.clearCallingIdentity(); try { - keyChainConnection = KeyChain.bind(mContext); + final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle); try { keyChainConnection.getService().installCaCertificate(pemCert); return true; + } catch (RemoteException e) { + Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); } finally { - if (keyChainConnection != null) { - keyChainConnection.close(); - keyChainConnection = null; - } + keyChainConnection.close(); } } catch (InterruptedException e1) { Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); + } finally { + Binder.restoreCallingIdentity(id); } return false; } @@ -2421,34 +2431,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { certBuffer)); } - public void uninstallCaCert(final byte[] certBuffer) { - mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); - TrustedCertificateStore certStore = new TrustedCertificateStore(); - String alias = null; - try { - X509Certificate cert = parseCert(certBuffer); - alias = certStore.getCertificateAlias(cert); - } catch (CertificateException ce) { - Log.e(LOG_TAG, "Problem creating X509Certificate", ce); - return; - } catch (IOException ioe) { - Log.e(LOG_TAG, "Problem reading certificate", ioe); - return; + public void uninstallCaCert(ComponentName who, String alias) { + if (who == null) { + mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); + } else { + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + } } + + final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); + final long id = Binder.clearCallingIdentity(); try { - KeyChainConnection keyChainConnection = KeyChain.bind(mContext); - IKeyChainService service = keyChainConnection.getService(); + final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle); try { - service.deleteCaCertificate(alias); + keyChainConnection.getService().deleteCaCertificate(alias); } catch (RemoteException e) { Log.e(LOG_TAG, "from CaCertUninstaller: ", e); } finally { keyChainConnection.close(); - keyChainConnection = null; } } catch (InterruptedException ie) { Log.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); + } finally { + Binder.restoreCallingIdentity(id); } } @@ -3454,12 +3461,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = Binder.clearCallingIdentity(); try { if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) { - pm.addCrossProfileIntentFilter(filter, true /*removable*/, callingUserId, - UserHandle.USER_OWNER); + pm.addCrossProfileIntentFilter(filter, callingUserId, UserHandle.USER_OWNER, + PackageManager.SET_BY_PROFILE_OWNER); } if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) { - pm.addCrossProfileIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER, - callingUserId); + pm.addCrossProfileIntentFilter(filter, UserHandle.USER_OWNER, callingUserId, + PackageManager.SET_BY_PROFILE_OWNER); } } catch (RemoteException re) { // Shouldn't happen @@ -3480,6 +3487,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = Binder.clearCallingIdentity(); try { pm.clearCrossProfileIntentFilters(callingUserId); + // If we want to support multiple managed profiles, we will have to only remove + // those that have callingUserId as their target. pm.clearCrossProfileIntentFilters(UserHandle.USER_OWNER); } catch (RemoteException re) { // Shouldn't happen @@ -3796,6 +3805,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("notifyLockTaskModeChanged can only be called by system"); + } + synchronized (this) { + final DevicePolicyData policy = getUserData(userHandle); + Bundle adminExtras = new Bundle(); + adminExtras.putBoolean(DeviceAdminReceiver.EXTRA_LOCK_TASK_ENTERING, isEnabled); + adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg); + for (ActiveAdmin admin : policy.mAdminList) { + boolean ownsDevice = isDeviceOwner(admin.info.getPackageName()); + boolean ownsProfile = (getProfileOwner(userHandle) != null + && getProfileOwner(userHandle).equals(admin.info.getPackageName())); + if (ownsDevice || ownsProfile) { + sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_CHANGED, + adminExtras, null); + } + } + } + } + + @Override public void setGlobalSetting(ComponentName who, String setting, String value) { final ContentResolver contentResolver = mContext.getContentResolver(); diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java index a97e7e4..989c2cd 100644 --- a/telecomm/java/android/telecomm/TelecommManager.java +++ b/telecomm/java/android/telecomm/TelecommManager.java @@ -16,7 +16,9 @@ package android.telecomm; +import android.content.ComponentName; import android.content.Context; +import android.os.RemoteException; import com.android.internal.telecomm.ITelecommService; @@ -45,4 +47,14 @@ public class TelecommManager { public static TelecommManager from(Context context) { return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE); } + + /** {@hide} */ + public ComponentName getSystemPhoneApplication() { + try { + return mService.getSystemPhoneApplication(); + } catch (RemoteException e) { + Log.e(TAG, e, "Error calling ITelecommService#getSystemPhoneApplication"); + return null; + } + } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 3fde36e..124a8ec 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -3035,28 +3035,6 @@ public class TelephonyManager { /** @hide */ @SystemApi - public int enableApnType(String type) { - try { - return getITelephony().enableApnType(type); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#enableApnType", e); - } - return PhoneConstants.APN_REQUEST_FAILED; - } - - /** @hide */ - @SystemApi - public int disableApnType(String type) { - try { - return getITelephony().disableApnType(type); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#disableApnType", e); - } - return PhoneConstants.APN_REQUEST_FAILED; - } - - /** @hide */ - @SystemApi public boolean enableDataConnectivity() { try { return getITelephony().enableDataConnectivity(); diff --git a/telephony/java/com/android/internal/telephony/IMms.aidl b/telephony/java/com/android/internal/telephony/IMms.aidl new file mode 100644 index 0000000..a745420 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IMms.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.app.PendingIntent; + +/** + * Service interface to handle MMS API requests + */ +interface IMms { + /** + * Send an MMS message + * + * @param callingPkg the package name of the calling app + * @param pdu the MMS message encoded in standard MMS PDU format + * @param locationUrl the optional location url for where this message should be sent to + * @param sentIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully sent, or failed + */ + void sendMessage(String callingPkg, in byte[] pdu, String locationUrl, + in PendingIntent sentIntent); + + /** + * Download an MMS message using known location and transaction id + * + * @param callingPkg the package name of the calling app + * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained + * from the MMS WAP push notification + * @param downloadedIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is downloaded, or the download is failed + */ + void downloadMessage(String callingPkg, String locationUrl, in PendingIntent downloadedIntent); +} diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 407da87..beee616 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -310,27 +310,6 @@ interface ITelephony { */ void disableLocationUpdatesUsingSubId(long subId); - - /** - * Enable a specific APN type. - */ - int enableApnType(String type); - - /** - * Disable a specific APN type. - */ - int disableApnType(String type); - - /** - * Enable a specific APN type with subscription. - */ - int enableApnTypeUsingSub(long subId, String type); - - /** - * Disable a specific APN type with subscription. - */ - int disableApnTypeUsingSub(long subId, String type); - /** * Allow mobile data connections. */ diff --git a/test-runner/Android.mk b/test-runner/Android.mk index 0d9e4f1..b12795c 100644 --- a/test-runner/Android.mk +++ b/test-runner/Android.mk @@ -20,7 +20,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := core core-junit framework +LOCAL_JAVA_LIBRARIES := core-libart core-junit framework LOCAL_STATIC_JAVA_LIBRARIES := junit-runner LOCAL_MODULE:= android.test.runner diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 17db1b4..a3b32b3 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -707,17 +707,8 @@ public class MockPackageManager extends PackageManager { * @hide */ @Override - public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId) { - throw new UnsupportedOperationException(); - } - - /** - * @hide - */ - @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId, - int targetUserId) { + public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, + int flags) { throw new UnsupportedOperationException(); } @@ -729,14 +720,6 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } - /** - * @hide - */ - @Override - public void clearForwardingIntentFilters(int sourceUserId) { - throw new UnsupportedOperationException(); - } - /** {@hide} */ public PackageInstaller getPackageInstaller() { throw new UnsupportedOperationException(); diff --git a/tests/JobSchedulerTestApp/res/layout/activity_main.xml b/tests/JobSchedulerTestApp/res/layout/activity_main.xml index 7f4961b..d3429ff 100644 --- a/tests/JobSchedulerTestApp/res/layout/activity_main.xml +++ b/tests/JobSchedulerTestApp/res/layout/activity_main.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical"> @@ -54,6 +54,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/constraints" + android:layout_margin="15dp" android:textSize="18dp"/> <LinearLayout android:layout_width="match_parent" @@ -83,43 +84,81 @@ </RadioGroup> </LinearLayout> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/timing"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="15dp" - android:textSize="17dp" - android:text="@string/delay"/> - <EditText - android:id="@+id/delay_time" - android:layout_width="60dp" - android:layout_height="wrap_content" - android:inputType="number"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/deadline" - android:textSize="17dp"/> - <EditText - android:id="@+id/deadline_time" - android:layout_width="60dp" - android:layout_height="wrap_content" - android:inputType="number"/> - </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/timing"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="15dp" + android:textSize="17dp" + android:text="@string/delay"/> + <EditText + android:id="@+id/delay_time" + android:layout_width="60dp" + android:layout_height="wrap_content" + android:inputType="number"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/deadline" + android:textSize="17dp"/> + <EditText + android:id="@+id/deadline_time" + android:layout_width="60dp" + android:layout_height="wrap_content" + android:inputType="number"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/charging_caption" + android:layout_marginRight="15dp"/> + <CheckBox + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/checkbox_charging" + android:text="@string/charging_text"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/idle_caption" + android:layout_marginRight="15dp"/> + <CheckBox + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/checkbox_idle" + android:text="@string/idle_mode_text"/> + </LinearLayout> </LinearLayout> <Button android:id="@+id/schedule_button" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="40dp" + android:layout_marginTop="20dp" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" android:onClick="scheduleJob" android:text="@string/schedule_job_button_text"/> + <Button + android:id="@+id/cancel_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" + android:onClick="cancelAllJobs" + android:text="@string/cancel_all_jobs_button_text"/> </LinearLayout> -</LinearLayout> +</ScrollView> diff --git a/tests/JobSchedulerTestApp/res/values/strings.xml b/tests/JobSchedulerTestApp/res/values/strings.xml index 824d4b1..eebfb19 100644 --- a/tests/JobSchedulerTestApp/res/values/strings.xml +++ b/tests/JobSchedulerTestApp/res/values/strings.xml @@ -20,9 +20,13 @@ limitations under the License. <string name="onstarttask">onStartTask</string> <string name="defaultparamtext">task params will show up here.</string> <string name="schedule_job_button_text">Schedule Job</string> + <string name="cancel_all_jobs_button_text">Cancel all</string> <string name="app_name">Job Scheduler Test</string> <string name="finish_job_button_text">taskFinished</string> - <string name="manual_sync_text">Manual Sync</string> + <string name="idle_mode_text">Requires device in idle mode.</string> + <string name="charging_caption">Charging:</string> + <string name="charging_text">Requires device plugged in.</string> + <string name="idle_caption">Idle:</string> <string name="constraints">Constraints</string> <string name="connectivity">Connectivity:</string> <string name="any">Any</string> diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java index 15050ef..e15929d 100644 --- a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java +++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java @@ -19,7 +19,9 @@ package com.android.demo.jobSchedulerApp; import android.app.Activity; import android.app.job.JobInfo; import android.app.job.JobParameters; +import android.app.job.JobScheduler; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; @@ -28,6 +30,7 @@ import android.os.Message; import android.os.Messenger; import android.text.TextUtils; import android.view.View; +import android.widget.CheckBox; import android.widget.EditText; import android.widget.RadioButton; import android.widget.TextView; @@ -60,7 +63,8 @@ public class MainActivity extends Activity { mDeadlineEditText = (EditText) findViewById(R.id.deadline_time); mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered); mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any); - + mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging); + mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle); mServiceComponent = new ComponentName(this, TestJobService.class); // Start service and provide it a way to communicate with us. Intent startServiceIntent = new Intent(this, TestJobService.class); @@ -79,6 +83,9 @@ public class MainActivity extends Activity { EditText mDeadlineEditText; RadioButton mWiFiConnectivityRadioButton; RadioButton mAnyConnectivityRadioButton; + CheckBox mRequiresChargingCheckBox; + CheckBox mRequiresIdleCheckbox; + ComponentName mServiceComponent; /** Service object to interact scheduled jobs. */ TestJobService mTestService; @@ -124,24 +131,32 @@ public class MainActivity extends Activity { String delay = mDelayEditText.getText().toString(); if (delay != null && !TextUtils.isEmpty(delay)) { - builder.setMinimumLatency(Long.valueOf(delay)); + builder.setMinimumLatency(Long.valueOf(delay) * 1000); } String deadline = mDeadlineEditText.getText().toString(); if (deadline != null && !TextUtils.isEmpty(deadline)) { - builder.setOverrideDeadline(Long.valueOf(deadline)); + builder.setOverrideDeadline(Long.valueOf(deadline) * 1000); } - boolean requiresUnmetered = mWiFiConnectivityRadioButton.isSelected(); - boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isSelected(); + boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked(); + boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked(); if (requiresUnmetered) { builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED); } else if (requiresAnyConnectivity) { builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY); } + builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked()); + builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked()); mTestService.scheduleJob(builder.build()); } + public void cancelAllJobs(View v) { + JobScheduler tm = + (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); + tm.cancelAll(); + } + /** * UI onclick listener to call jobFinished() in our service. */ diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml index db2efc3..890214f 100644 --- a/tests/VectorDrawableTest/AndroidManifest.xml +++ b/tests/VectorDrawableTest/AndroidManifest.xml @@ -33,6 +33,16 @@ </intent-filter> </activity> <activity + android:name="BitmapDrawableDupe" + android:label="Bitmap Performance of clones" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="com.android.test.dynamic.TEST" /> + </intent-filter> + + </activity> + <activity android:name="VectorDrawableAnimation" android:label="VectorTestAnimation" > <intent-filter> diff --git a/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg Binary files differnew file mode 100644 index 0000000..dc8c197 --- /dev/null +++ b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java new file mode 100644 index 0000000..36c8f2b --- /dev/null +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.test.dynamic; + +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.drawable.BitmapDrawable; +import android.os.Bundle; +import android.widget.TextView; +import android.widget.Button; +import android.widget.GridLayout; +import android.widget.ScrollView; + +import java.text.DecimalFormat; + +@SuppressWarnings({"UnusedDeclaration"}) +public class BitmapDrawableDupe extends Activity { + private static final String LOGCAT = "VectorDrawable1"; + protected int[] icon = { + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + R.drawable.bitmap_drawable01, + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ScrollView scrollView = new ScrollView(this); + GridLayout container = new GridLayout(this); + scrollView.addView(container); + container.setColumnCount(5); + container.setBackgroundColor(0xFF888888); + + DecimalFormat df = new DecimalFormat("#.##"); + long time = android.os.SystemClock.elapsedRealtimeNanos(); + for (int i = 0; i < icon.length; i++) { + Button button = new Button(this); + button.setWidth(200); + button.setBackgroundResource(icon[i]); + container.addView(button); + } + + setContentView(scrollView); + time = android.os.SystemClock.elapsedRealtimeNanos()-time; + TextView t = new TextView(this); + t.setText("avgS=" + df.format(time / (icon.length * 1000000.)) + " ms"); + container.addView(t); + } +} diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index f10904c..3d93bbe 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -589,11 +589,11 @@ static bool applyFileOverlay(Bundle *bundle, if (bundle->getVerbose()) { printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string()); } - size_t baseIndex = UNKNOWN_ERROR; + ssize_t baseIndex = -1; if (baseSet->get() != NULL) { baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex)); } - if (baseIndex < UNKNOWN_ERROR) { + if (baseIndex >= 0) { // look for same flavor. For a given file (strings.xml, for example) // there may be a locale specific or other flavors - we want to match // the same flavor. @@ -619,10 +619,10 @@ static bool applyFileOverlay(Bundle *bundle, for (size_t overlayGroupIndex = 0; overlayGroupIndex<overlayGroupSize; overlayGroupIndex++) { - size_t baseFileIndex = + ssize_t baseFileIndex = baseGroup->getFiles().indexOfKey(overlayFiles. keyAt(overlayGroupIndex)); - if (baseFileIndex < UNKNOWN_ERROR) { + if (baseFileIndex >= 0) { if (bundle->getVerbose()) { printf("found a match (" ZD ") for overlay file %s, for flavor %s\n", (ZD_TYPE) baseFileIndex, @@ -1363,7 +1363,11 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil if (split->isBase()) { resFile = flattenedTable; - finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + if (err != NO_ERROR) { + fprintf(stderr, "Generated resource table is corrupt.\n"); + return err; + } } else { sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), AaptGroupEntry(), String8()); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index efbba40..1a9f1b9 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2292,8 +2292,14 @@ uint32_t ResourceTable::getCustomResourceWithCreation( if (resId != 0 || !createIfNotFound) { return resId; } - String16 value("false"); + if (mAssetsPackage != package) { + mCurrentXmlPos.warning("creating resource for external package %s: %s/%s.", + String8(package).string(), String8(type).string(), String8(name).string()); + mCurrentXmlPos.printf("This will be an error in a future version of AAPT."); + } + + String16 value("false"); status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true); if (status == NO_ERROR) { resId = getResId(package, type, name); @@ -3062,8 +3068,9 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& for (size_t i = 0; i < N; ++i) { if (!validResources[i]) { sp<ConfigList> c = t->getOrderedConfigs().itemAt(i); - fprintf(stderr, "%s: no entries written for %s/%s\n", log_prefix, - String8(typeName).string(), String8(c->getName()).string()); + fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix, + String8(typeName).string(), String8(c->getName()).string(), + Res_MAKEID(p->getAssignedId() - 1, ti, i)); missing_entry = true; } } diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk index 08486e6..1942831 100644 --- a/tools/layoutlib/Android.mk +++ b/tools/layoutlib/Android.mk @@ -30,8 +30,8 @@ LOCAL_JAVACFLAGS := -source 6 -target 6 built_framework_dep := $(call java-lib-deps,framework-base) built_framework_classes := $(call java-lib-files,framework-base) -built_core_dep := $(call java-lib-deps,core) -built_core_classes := $(call java-lib-files,core) +built_core_dep := $(call java-lib-deps,core-libart) +built_core_classes := $(call java-lib-files,core-libart) built_ext_dep := $(call java-lib-deps,ext) built_ext_classes := $(call java-lib-files,ext) |
