Merge "initial tuning"
diff --git a/Android.mk b/Android.mk
index 216d8a2d4..56e9df6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -344,6 +344,7 @@
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 @@
$(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_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 @@
-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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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;
}
- *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;
+
+ 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;
+ }
+
+ *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 @@
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 @@
if (name) {
*name = String8(String16(data.name, data.nameLen));
}
- return 0;
+ return NO_ERROR;
}
- int package_id(const AssetManager& am)
- {
- return (am.getResources().getBasePackageId(0)) << 24;
- }
-
- 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, "");
- NEXT(buf, i, o);
- print("", "base crc", i, o, "");
-
- NEXT(buf, i, o);
- print("", "overlay crc", i, o, "");
-
- if (buf.nextPath(path, &o, &e) < 0) {
- // printe done from IdmapBuffer::nextPath
- return -1;
+ err = buf.nextUint32(&i);
+ if (err != NO_ERROR) {
+ return err;
}
- print_path("", "base path", o, e, "%s", path);
+ print("", "version", i, "");
+
+ err = buf.nextUint32(&i);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ print("", "base crc", i, "");
+
+ err = buf.nextUint32(&i);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ print("", "overlay crc", i, "");
+
+ err = buf.nextPath(path);
+ if (err != NO_ERROR) {
+ // printe done from IdmapBuffer::nextPath
+ return err;
+ }
+ 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 {
+ err = buf.nextUint16(&data16);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ print("", "types count", static_cast<uint32_t>(data16), "");
+
+ 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, "");
+
+ 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;
+ }
+
+ uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i);
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;
+ String8 name;
+ err = resource_metadata(am, resID, NULL, &type, &name);
+ if (err != NO_ERROR) {
+ return err;
}
- print("", "type offset", i, o, "absolute offset 0x%02x, %s",
- i + IDMAP_HEADER_SIZE, type.string());
- types.add(numeric_type);
+ print("", "entry", data32, "%s/%s", type.string(), name.string());
}
}
- return 0;
- }
-
- 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;
- }
- print("", "entry", i, o, "%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 @@
* 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 @@
// 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 @@
* @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 @@
* @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 @@
}
}
- /**
- * @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 @@
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 @@
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 @@
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 @@
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 @@
= "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 @@
}
/**
+ * 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 @@
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 @@
*
* @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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
}
/**
+ * 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 @@
* 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 @@
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 @@
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 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 @@
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 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 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 @@
* {@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.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.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.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 @@
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 @@
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 @@
mOnlyCoreApps = onlyCoreApps;
}
+ public void setDisplayMetrics(DisplayMetrics metrics) {
+ mMetrics = metrics;
+ }
+
private static final boolean isPackageFilename(File file) {
return isPackageFilename(file.getName());
}
@@ -480,23 +496,21 @@
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 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 @@
}
}
- 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 @@
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 @@
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 @@
// 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 @@
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 @@
* {@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 @@
} 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");
+ pkg.mCertificates = null;
+ pkg.mSignatures = null;
+ pkg.mSigningKeys = null;
+
+ collectCertificates(pkg, new File(pkg.codePath), flags);
+
+ if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+ for (String splitCodePath : pkg.splitCodePaths) {
+ collectCertificates(pkg, new File(splitCodePath), flags);
+ }
}
}
- private boolean collectCertificatesInternal(Package pkg, int flags) {
- pkg.mSignatures = null;
+ private static void collectCertificates(Package pkg, File apkFile, int flags)
+ throws PackageParserException {
+ final String apkPath = apkFile.getAbsolutePath();
- 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);
- }
- }
-
+ StrictJarFile jarFile = null;
try {
- StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
+ jarFile = new StrictJarFile(apkPath);
- Certificate[][] certs = null;
+ // 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 ((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;
+ final List<ZipEntry> toVerify = new ArrayList<>();
+ toVerify.add(manifestEntry);
+
+ // 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();
+
+ if (entry.isDirectory()) continue;
+ if (entry.getName().startsWith("META-INF/")) continue;
+ if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue;
+
+ toVerify.add(entry);
}
- 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());
- }
- }
+ }
+
+ // 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());
}
- } else {
- Iterator<ZipEntry> entries = jarFile.iterator();
- while (entries.hasNext()) {
- final ZipEntry je = entries.next();
- if (je.isDirectory()) continue;
- final String name = je.getName();
-
- if (name.startsWith("META-INF/"))
- continue;
-
- if (ANDROID_MANIFEST_FILENAME.equals(name)) {
- pkg.manifestDigest =
- ManifestDigest.fromInputStream(jarFile.getInputStream(je));
+ 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());
}
-
- 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 (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
+ } 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 "
- + je.getName() + "; ignoring!");
- jarFile.close();
- mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
- return false;
- }
- }
+ + entry.getName());
}
}
}
- 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;
- }
-
- // 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());
- }
-
- } 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 @@
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 @@
}
}
- 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 @@
}
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 @@
}
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 @@
}
}
- 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 @@
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 @@
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 @@
}
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 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 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 @@
/**
* 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 @@
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 @@
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 @@
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 535bbe24..8142670 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -699,15 +699,15 @@
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 @@
/**
* 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 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
*/
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 @@
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 @@
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;
+ if (startValues == null) {
+ return null;
}
-
- @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);
- }
+ 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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
* <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 @@
* {@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 @@
@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 @@
@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 @@
}
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.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 @@
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 @@
*/
private void setPressed(boolean pressed, float x, float y) {
if (pressed) {
- setDrawableHotspot(x, y);
+ drawableHotspotChanged(x, y);
}
setPressed(pressed);
@@ -9149,7 +9135,7 @@
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 @@
}
/**
+ * 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.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 @@
@Override
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
- if (mThumb != null) mThumb.jumpToCurrentState();
+
+ if (mThumb != null) {
+ mThumb.jumpToCurrentState();
+ }
}
@Override
@@ -361,29 +363,12 @@
}
}
- /** @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 @@
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 @@
@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 @@
}
}
- /** @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.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 @@
}
}
- /** @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.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 @@
}
}
- /** @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.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 @@
}
}
- /** @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 @@
}
}
- /** @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 @@
}
}
- /** @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 @@
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 @@
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 @@
}
}
- /** @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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
(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 a4337ccc..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 @@
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 @@
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 @@
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 @@
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;
- }
-
- 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);
- }
-
- 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;
- }
-
- 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);
- }
-
- 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);
- }
-
- 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);
- }
-
- static void endRecording(JNIEnv* env, jobject, jlong pictHandle) {
- AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
- pict->endRecording();
- }
-};
-
-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}
-};
-
-#include <android_runtime/AndroidRuntime.h>
-
-#define REG(env, name, array) \
- result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
- SK_ARRAY_COUNT(array)); \
- if (result < 0) return result
-
-int register_android_graphics_Picture(JNIEnv* env) {
- int result;
-
- REG(env, "android/graphics/Picture", gPictureMethods);
-
- return result;
}
+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);
}
+void Picture::endRecording() {
+ if (NULL != mRecorder.get()) {
+ mPicture.reset(mRecorder->endRecording());
+ mRecorder.reset(NULL);
+ }
+}
+int Picture::width() const {
+ if (NULL != mPicture.get()) {
+ SkASSERT(mPicture->width() == mWidth);
+ SkASSERT(mPicture->height() == mHeight);
+ }
+
+ return mWidth;
+}
+
+int Picture::height() const {
+ if (NULL != mPicture.get()) {
+ SkASSERT(mPicture->width() == mWidth);
+ SkASSERT(mPicture->height() == mHeight);
+ }
+
+ return mHeight;
+}
+
+Picture* Picture::CreateFromStream(SkStream* stream) {
+ Picture* newPict = new Picture;
+
+ newPict->mPicture.reset(SkPicture::CreateFromStream(stream));
+ if (NULL != newPict->mPicture.get()) {
+ newPict->mWidth = newPict->mPicture->width();
+ newPict->mHeight = newPict->mPicture->height();
+ }
+
+ return newPict;
+}
+
+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);
+ }
+}
+
+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);
+ }
+}
+
+SkPicture* Picture::makePartialCopy() const {
+ SkASSERT(NULL != mRecorder.get());
+
+ 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
similarity index 85%
rename from core/jni/android/graphics/AndroidPicture.h
rename to 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 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 @@
int height() const;
- static AndroidPicture* CreateFromStream(SkStream* stream);
+ static Picture* CreateFromStream(SkStream* stream);
void serialize(SkWStream* stream) const;
@@ -60,4 +62,6 @@
// 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 @@
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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
#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 @@
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_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 @@
{ "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 @@
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 @@
}
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 @@
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 @@
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 @@
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 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
deleted file mode 100644
index c79ce92..0000000
--- a/core/res/res/drawable-hdpi/ic_corp_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png
deleted file mode 100644
index 0059e09..0000000
--- a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_corp_badge.png b/core/res/res/drawable-mdpi/ic_corp_badge.png
deleted file mode 100644
index c1447fe..0000000
--- a/core/res/res/drawable-mdpi/ic_corp_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png
deleted file mode 100644
index 5ff8c5d..0000000
--- a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_corp_badge.png b/core/res/res/drawable-xhdpi/ic_corp_badge.png
deleted file mode 100644
index 2d3d748..0000000
--- a/core/res/res/drawable-xhdpi/ic_corp_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png
deleted file mode 100644
index dc5716d..0000000
--- a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_corp_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_badge.png
deleted file mode 100644
index 430e63b..0000000
--- a/core/res/res/drawable-xxhdpi/ic_corp_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png
deleted file mode 100644
index cc00dd8..0000000
--- a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png
+++ /dev/null
Binary files differ
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 @@
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..37e9b157 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 @@
<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 @@
<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
deleted file mode 100644
index 4d93a86..0000000
--- a/docs/html/images/tools/android-studio.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/tools/laptop-studio.png b/docs/html/images/tools/laptop-studio.png
new file mode 100644
index 0000000..3684ff0
--- /dev/null
+++ b/docs/html/images/tools/laptop-studio.png
Binary files differ
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 @@
<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 @@
<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 @@
</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 @@
<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 @@
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 @@
<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 @@
<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 @@
<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 @@
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 @@
<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 @@
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 @@
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 @@
<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.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 @@
<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 @@
<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 @@
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 @@
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 @@
<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
new file mode 100644
index 0000000..9613a92
--- /dev/null
+++ b/docs/html/preview/images/hun-example.png
Binary files differ
diff --git a/docs/html/preview/images/hun-example@2x.png b/docs/html/preview/images/hun-example@2x.png
new file mode 100644
index 0000000..3cb8f5b
--- /dev/null
+++ b/docs/html/preview/images/hun-example@2x.png
Binary files differ
diff --git a/docs/html/preview/images/l-dev-prev.png b/docs/html/preview/images/l-dev-prev.png
new file mode 100644
index 0000000..95bad8c
--- /dev/null
+++ b/docs/html/preview/images/l-dev-prev.png
Binary files differ
diff --git a/docs/html/preview/images/managed_apps_launcher@2.png b/docs/html/preview/images/managed_apps_launcher@2x.png
similarity index 100%
rename from docs/html/preview/images/managed_apps_launcher@2.png
rename to docs/html/preview/images/managed_apps_launcher@2x.png
Binary files differ
diff --git a/docs/html/preview/index.html b/docs/html/preview/index.html
new file mode 100644
index 0000000..368db84
--- /dev/null
+++ b/docs/html/preview/index.html
@@ -0,0 +1,361 @@
+<!DOCTYPE html>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<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;
+}
+</style>
+
+<style>
+#footer {
+ display: none;
+}
+.content-footer {
+ display: none;
+}
+</style>
+
+ <div class="landing-rest-of-page">
+ <div class="landing-section" style="padding-top:30px">
+ <div class="wrap">
+ <div class="landing-section-header">
+ <div class="landing-h1">Android L Developer Preview</div>
+ <div class="landing-subhead">
+ 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-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">
+ <p>A New UI Design</p>
+ <p class="landing-small">
+ Create a consistent experience across mobile and the web with
+ material design, the new Google-wide standard.
+ </p>
+ <p class="landing-small">
+ <a href="/preview/material/index.html">Learn about material</a>
+ </p>
+ </div>
+ <div class="col-4">
+ <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">
+ <p style="width:230px">Enhanced Notifications</p>
+ <p class="landing-small">
+ Get more control over where notifications appear,
+ how they look, and automatic syncing to non-handheld devices.
+ </p>
+ <p class="landing-small">
+ <a href="/preview/api-overview.html#UI">Learn more</a>
+ </p>
+ </div>
+ <div class="col-4">
+ <p>Project Volta</p>
+ <p class="landing-small">
+ We've tuned the platform to be more energy efficient and
+ to give you more control over resource usage.
+ </p>
+ <p class="landing-small">
+ <a href="/preview/api-overview.html#Power">Learn more</a>
+ </p>
+ </div>
+ </div>
+ <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></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="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>
+ <p class="landing-small">
+ 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="https://code.google.com/p/android-developer-preview/">
+ Report Issues</a>
+ </p>
+ <p></p>
+ </div>
+ </div>
+ <div class="col-3-wide">
+ <a target="_blank" href="http://plus.google.com">
+ <img class="landing-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="">
+ </a>
+ <div class="landing-social-copy">
+ <p>Google+ </p>
+ <p class="landing-small">
+ Join the community of Android developers testing out the L Developer Preview and
+ share your thoughts and experiences.
+ </p><p class="landing-small">
+ <a target="_blank" href="http://plus.google.com">
+ Discuss on Google+</a>
+ </p>
+ </div>
+ </div>
+ <div class="col-3-wide">
+ <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>
+ <p class="landing-small">
+ Updates to the L Developer Preview are delivered
+ in the Android SDK Manager. Check back periodically
+ for news about the changes.
+ </p>
+ <p class="landing-small">
+ <a target="_blank" href="/preview/support.html">Get Support</a>
+ </p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
+ <div class="layout-content-col col-16" style="padding-top:4px">
+ <style>#___plusone_0 {float:right !important;}</style>
+ <div class="g-plusone" data-size="medium"></div>
+ </div>
+ </div>
+ <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
+ <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>
+ </div> <!-- end landing-body-content -->
+
+ <script>
+ $("a.landing-down-arrow").on("click", function(e) {
+ $("body").animate({
+ scrollTop: $(".preview-hero").height() + 76
+ }, 1000, "easeOutQuint");
+ e.preventDefault();
+ });
+ </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/preview/index.jd b/docs/html/preview/index.jd
deleted file mode 100644
index e44e9f3..0000000
--- a/docs/html/preview/index.jd
+++ /dev/null
@@ -1,236 +0,0 @@
-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
-
-<style>
-.fullpage>#footer,
-#jd-content>.content-footer.wrap {
- display:none;
-}
-</style>
-
-<style>
-#footer {
- display: none;
-}
-.content-footer {
- display: none;
-}
-</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="wrap">
- <div class="landing-section-header">
- <div class="landing-h1">See What's New</div>
- <div class="landing-subhead">
- Take advantage of all the new capabilities, which are focused on design and performance.
- </div>
- </div>
-
- <div class="landing-body">
-
- <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
- material design, the new Google-wide standard.
- </p>
- <p class="landing-small">
- <a href="/preview/material/index.html">Learn about material</a>
- </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,
- how they look, and automatic syncing to non-handheld devices.
- </p>
- <p class="landing-small">
- <a href="/preview/api-overview.html#UI">Learn more</a>
- </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
- to give you more control over resource usage.
- </p>
- <p class="landing-small">
- <a href="/preview/api-overview.html#Power">Learn more</a>
- </p>
- </div>
- </div>
- <p>See the <a href="{@docRoot}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 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>
- <div class="landing-social-copy">
- <p>Issue Tracker</p>
- <p class="landing-small">
- 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!">
- Report Issues</a>
- </p>
- <p></p>
- </div>
- </div>
- <div class="col-3-wide">
- <a target="_blank" href="http://plus.google.com">
- <img class="landing-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="">
- </a>
- <div class="landing-social-copy">
- <p>Google+ </p>
- <p class="landing-small">
- Join the community of Android developers testing out the L Developer Preview and
- share your thoughts and experiences.
- </p><p class="landing-small">
- <a target="_blank" href="http://plus.google.com">
- Discuss on Google+</a>
- </p>
- </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>
- <div class="landing-social-copy">
- <p>Support and Updates</p>
- <p class="landing-small">
- Updates to the L Developer Preview are delivered
- in the Android SDK Manager. Check back periodically
- for news about the changes.
- </p>
- <p class="landing-small">
- <a target="_blank" href="{@docRoot}preview/support.html">Get Support</a>
- </p>
- </div>
- </div>
- </div>
- </div>
- </div> <!-- end .wrap -->
- </div>
-
- <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
- <div class="layout-content-col col-16" style="padding-top:4px">
- <style>#___plusone_0 {float:right !important;}</style>
- <div class="g-plusone" data-size="medium"></div>
- </div>
- </div>
- <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
- <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>
- </div> <!-- end landing-body-content -->
-
- <script>
- $("a.landing-down-arrow").on("click", function(e) {
- $("body").animate({
- scrollTop: $(".preview-hero").height() + 76
- }, 1000, "easeOutQuint");
- e.preventDefault();
- });
- </script>
\ 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 @@
<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 @@
<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 @@
}
native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
path.ni(), hOffset, vOffset,
- paint.mBidiFlags, paint.mNativePaint);
+ paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
}
/**
@@ -1790,7 +1790,7 @@
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 @@
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 @@
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 @@
* 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 @@
* 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 @@
*/
@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 @@
}
}
- 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 @@
@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 @@
private long mExitAnimationEnd;
private Drawable mLastDrawable;
- private Insets mInsets = Insets.NONE;
-
// overrides from Drawable
@Override
@@ -118,7 +116,10 @@
*/
@Override
public Insets getOpticalInsets() {
- return mInsets;
+ if (mCurrDrawable != null) {
+ return mCurrDrawable.getOpticalInsets();
+ }
+ return Insets.NONE;
}
@Override
@@ -203,9 +204,6 @@
}
if (mCurrDrawable != null) {
mCurrDrawable.setBounds(bounds);
-
- // Must obtain optical insets after setting bounds.
- mInsets = mCurrDrawable.getOpticalInsets();
}
}
@@ -422,15 +420,9 @@
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 @@
}
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 @@
}
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 @@
#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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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.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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
#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 @@
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 @@
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 (*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));
+
+ if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
+ ALOGW("idmap: header too small (%d bytes)", (uint32_t) size);
+ return false;
+ }
+
+ 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;
- }
- if (typeCount > size) {
- ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size);
- return UNKNOWN_ERROR;
- }
- const uint32_t typeOffset = map[type];
- if (typeOffset == 0) {
- *outValue = 0;
- return NO_ERROR;
- }
- 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;
- }
- const uint32_t entryCount = map[typeOffset];
- const uint32_t entryOffset = map[typeOffset + 1];
- if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) {
- *outValue = 0;
- 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;
- return NO_ERROR;
- }
- *outValue = map[index];
+ bool hasEntries() const {
+ if (mData == NULL) {
+ return false;
+ }
- return NO_ERROR;
-}
+ return (dtohs(*mData) > 0);
+ }
-static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId)
-{
- if (!assertIdmapHeader(map, mapSize)) {
- 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));
}
- if (mapSize <= IDMAP_HEADER_SIZE + 1) {
- ALOGW("corrupt idmap: map size %d too short\n", (int)mapSize);
- return UNKNOWN_ERROR;
+
+ uint8_t targetTypeId() const {
+ if (mData == NULL) {
+ return 0;
+ }
+ return dtohs(mData[0]);
}
- uint32_t typeCount = *(map + IDMAP_HEADER_SIZE);
- if (typeCount == 0) {
- ALOGW("corrupt idmap: no types\n");
- return UNKNOWN_ERROR;
+
+ uint8_t overlayTypeId() const {
+ if (mData == NULL) {
+ return 0;
+ }
+ return dtohs(mData[1]);
}
- if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) {
- ALOGW("corrupt idmap: number of types %u extends past idmap size %d\n", typeCount, (int)mapSize);
- 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");
+
+ 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;
}
- // 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);
+ 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;
+ }
+
+private:
+ const uint16_t* mData;
+};
+
+status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) {
+ if (!assertIdmapHeader(idmap, size)) {
return UNKNOWN_ERROR;
}
- *outId = (map[offset] >> 24) & 0x000000ff;
+ size -= ResTable::IDMAP_HEADER_SIZE_BYTES;
+ if (size < sizeof(uint16_t) * 2) {
+ ALOGE("idmap: too small to contain any mapping");
+ return UNKNOWN_ERROR;
+ }
+
+ 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;
+ }
+
+ uint16_t mapCount = dtohs(*(data++));
+ if (mapCount == 0) {
+ ALOGE("idmap: no mappings");
+ return UNKNOWN_ERROR;
+ }
+
+ if (mapCount > 255) {
+ ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount);
+ }
+
+ 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();
+ }
+
+ if (outPackageId != NULL) {
+ *outPackageId = static_cast<uint8_t>(targetPackageId);
+ }
return NO_ERROR;
}
@@ -2726,7 +2808,7 @@
free(resourceIDMap);
}
- ResTable* const owner;
+ const ResTable* const owner;
void* ownedData;
const ResTable_header* header;
size_t size;
@@ -2739,6 +2821,17 @@
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 @@
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 @@
: 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 @@
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 @@
}
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
{
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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;
- }
-
- 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;
- }
- }
- 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;
- }
- }
-
- return true;
+ Entry entry;
+ status_t err = getEntry(grp, t, e, NULL, &entry);
+ if (err != NO_ERROR) {
+ return false;
}
- 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 = 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;
}
ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
@@ -3471,15 +3593,6 @@
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 @@
}
// 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;
+ desiredConfig.density = density;
}
- 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;
- }
-
- if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) {
- if (!mayBeBag) {
- ALOGW("Requesting resource 0x%x failed because it is complex\n",
- resID);
- }
- continue;
- }
-
- 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;
- }
-
- const Res_value* item =
- (const Res_value*)(((const uint8_t*)type) + offset);
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(type->config);
-
- if (outSpecFlags != NULL) {
- if (typeClass->typeSpecFlags != NULL) {
- *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
- } else {
- *outSpecFlags = -1;
- }
- }
-
- 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;
+ 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;
}
- 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;
+ if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
+ if (!mayBeBag) {
+ ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
}
-
- 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;
+ return BAD_VALUE;
}
-out:
- if (overrideConfig != NULL) {
- free(overrideConfig);
+ const Res_value* value = reinterpret_cast<const Res_value*>(
+ reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
+
+ outValue->size = dtohs(value->size);
+ outValue->res0 = value->res0;
+ outValue->dataType = value->dataType;
+ outValue->data = dtohl(value->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.
+ if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
+ ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
+ return BAD_VALUE;
}
- return rc;
+ 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 (outSpecFlags != NULL) {
+ *outSpecFlags = entry.specFlags;
+ }
+
+ if (outConfig != NULL) {
+ *outConfig = entry.config;
+ }
+
+ return entry.package->header->index;
}
ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
@@ -3721,29 +3754,25 @@
PackageGroup* const grp = mPackageGroups[p];
if (grp == NULL) {
ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
- return false;
- }
-
- if (t >= (int)grp->typeCount) {
- ALOGW("Type identifier 0x%x is larger than type count 0x%x",
- t+1, (int)grp->typeCount);
return BAD_INDEX;
}
- const Package* const basePackage = grp->packages[0];
+ const TypeList& typeConfigs = grp->types[t];
+ if (typeConfigs.isEmpty()) {
+ ALOGW("Type identifier 0x%x does not exist.", t+1);
+ return BAD_INDEX;
+ }
- 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 @@
// 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;
+ TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+
+ // Now collect all bag attributes
+ Entry entry;
+ status_t err = getEntry(grp, t, e, &mParams, &entry);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ 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;
+
+ size_t N = count;
+
+ 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;
- TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+ if (parent) {
+ uint32_t resolvedParent = parent;
- ResTable_config bestConfig;
- memset(&bestConfig, 0, sizeof(bestConfig));
-
- // 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;
- }
+ // 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 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;
+ 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 ((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;
- }
-
- if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) {
- continue;
- }
- bestConfig = type->config;
- if (set) {
- free(set);
- set = NULL;
- }
-
- 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;
-
- 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));
- 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;
- }
-
- 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 (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 {
- set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
- if (set == NULL) {
- return NO_MEMORY;
- }
+ TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
set->numAttrs = 0;
- set->availAttrs = N;
- set->typeSpecFlags = 0;
}
-
- if (typeClass->typeSpecFlags != NULL) {
- set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
- } else {
- set->typeSpecFlags = -1;
+ 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;
+ }
- // 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));
+ set->typeSpecFlags |= entry.specFlags;
- 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++;
+ // 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));
- 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 (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++;
- 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;
-
- 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));
-
- // On to the next!
+ 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++;
- pos++;
- const size_t size = dtohs(map->value.size);
- curOff += size + sizeof(*map)-sizeof(map->value);
- };
- if (curEntry > set->numAttrs) {
- set->numAttrs = 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;
+
+ 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;
+ }
+
+ 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);
+ };
+
+ if (curEntry > set->numAttrs) {
+ set->numAttrs = curEntry;
}
// And this is it...
@@ -4154,80 +4128,63 @@
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()));
+ 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;
}
- TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei));
+ 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;
+ }
- 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",
- String8(group->name).string(), ti));
- }
-
- 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;
-
- 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 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;
- }
-
- 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;
- }
+ 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;
}
- return Res_MAKEID(group->id-1, ti, i);
+
+ 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;
+
+ if (fakePublic) {
+ *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
+ }
+ }
+ return resId;
+ }
}
}
}
+ break;
}
-
return 0;
}
@@ -5260,6 +5217,18 @@
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 @@
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 @@
}
}
-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
+ memset(&bestConfig, 0, sizeof(bestConfig));
- 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;
+ // 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];
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(thisType->config);
+ int realEntryIndex = entryIndex;
+ int realTypeIndex = typeIndex;
+ bool currentTypeIsOverlay = false;
- 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()));
-
- // 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"));
- continue;
- }
-
- // Check if there is the desired entry in this type.
-
- 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));
-
- uint32_t thisOffset = dtohl(eindex[entryIndex]);
- if (thisOffset == ResTable_type::NO_ENTRY) {
- TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n"));
- continue;
- }
-
- 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"));
+ // 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;
}
- type = thisType;
- offset = thisOffset;
- bestConfig = thisConfig;
- TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
- if (!config) break;
+ 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;
+ }
+
+ // Aggregate all the flags for each package that defines this entry.
+ if (typeSpec->typeSpecFlags != NULL) {
+ specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]);
+ } else {
+ specFlags = -1;
+ }
+
+ 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;
+ }
+
+ ResTable_config thisConfig;
+ thisConfig.copyFromDtoH(thisType->config);
+
+ // 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;
+ }
+
+ 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 @@
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) {
+ uint32_t id = dtohl(pkg->id);
+ KeyedVector<uint8_t, IdmapEntries> idmapEntries;
- package = new Package(this, header, pkg);
- if (package == NULL) {
+ 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 >= 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++;
+ }
+
+ PackageGroup* group = NULL;
+ Package* package = new Package(this, header, pkg);
+ if (package == NULL) {
+ 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);
+ }
+
+ 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);
+ }
+
+ size_t idx = mPackageMap[id];
+ if (idx == 0) {
+ idx = mPackageGroups.size() + 1;
+
+ 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);
}
- 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);
- }
-
- 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 (id == 0) {
- // This is a library so assign an ID
- id = mNextPackageId++;
- }
-
- size_t idx = mPackageMap[id];
- if (idx == 0) {
- idx = mPackageGroups.size()+1;
-
- 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);
- }
-
- //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;
-
- mPackageMap[id] = (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 {
- group = mPackageGroups.itemAt(idx-1);
- if (group == NULL) {
- return (mError=UNKNOWN_ERROR);
- }
- }
- 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 @@
}
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 @@
(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 @@
return (mError=BAD_TYPE);
}
- while (package->types.size() < typeSpec->id) {
- package->types.add(NULL);
+ 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);
}
- 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);
- }
- 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 @@
}
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 @@
(((const uint8_t*)chunk) + csize);
}
- if (group->typeCount == 0) {
- group->typeCount = package->types.size();
- }
-
return NO_ERROR;
}
@@ -5818,6 +5873,12 @@
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 @@
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 @@
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;
}
-#endif
+
+ 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));
+ }
}
- 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 @@
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;
+ 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]);
}
- }
- 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);
- }
+ 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 @@
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 @@
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]));
- } else {
- printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
- }
- }
+ 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;
}
- 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);
+ 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;
}
- 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 & ((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);
- }
- printf(" resource 0x%08x %s:%s/%s: ", resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string());
+ 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 {
- printf(" INVALID RESOURCE 0x%08x: ", resID);
+ type8 = String8(resName.type, resName.typeLen);
}
- 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;
+ if (resName.name8 != NULL) {
+ name8 = String8(resName.name8, resName.nameLen);
} 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);
+ 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);
+ }
+ 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;
+ }
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
- printf(" (PUBLIC)");
- }
- printf("\n");
+ 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;
+ }
- 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);
- }
+ 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(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(" 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)
-# Build the unit tests.
-test_src_files := \
- BackupData_test.cpp \
- ObbFile_test.cpp \
- ZipUtils_test.cpp \
- ResourceTypes_test.cpp
+LOCAL_MODULE := libandroidfw_tests
-shared_libraries := \
+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)
+
+LOCAL_MODULE := libandroidfw_tests
+
+LOCAL_SRC_FILES := $(testFiles) \
+ BackupData_test.cpp \
+ ObbFile_test.cpp
+
+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 @@
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 @@
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 0e47c6e2..fc4d40b 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -308,6 +308,10 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 = 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
~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 @@
, prepareTextures(mode == MODE_FULL)
, damageAccumulator(NullDamageAccumulator::instance())
, renderer(0)
+ , errorHandler(0)
{}
const TraversalMode mode;
@@ -78,6 +88,7 @@
// 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 @@
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 @@
* 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 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 @@
switch (audioFormat) {
case ENCODING_PCM_8BIT:
return 1;
- case ENCODING_PCM_FLOAT:
- return 4;
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 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 @@
* @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 @@
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 @@
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..cf6d35ee8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2534,6 +2534,9 @@
// 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 @@
/** @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 @@
// 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 @@
//--------------
// 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 @@
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 @@
}
}
- 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 @@
* @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 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 @@
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.net.Uri;
import android.provider.BaseColumns;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* <p>
@@ -380,6 +382,81 @@
/** 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 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 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 @@
/** 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 @@
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 @@
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 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.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 @@
* @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 @@
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 @@
!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 @@
/** Task bar colors */
public int taskBarViewDefaultBackgroundColor;
- public int taskBarViewDefaultTextColor;
public int taskBarViewLightTextColor;
public int taskBarViewDarkTextColor;
public int taskBarViewHighlightColor;
@@ -202,8 +201,6 @@
// 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 @@
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 @@
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 @@
(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 @@
*/
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
if (useLayers) {
anim.withLayer();
}
- anim.setStartDelay(0)
+ anim.setStartDelay(toTransform.startDelay)
.setDuration(duration)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
.start();
@@ -248,6 +248,7 @@
// Fade the view out and slide it away
toTransform.alpha = 0f;
toTransform.translationY += 200;
+ toTransform.translationZ = 0;
}
/**
@@ -585,19 +586,25 @@
}
@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 @@
/* 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 TaskViewTransform(TaskViewTransform o) {
+ startDelay = o.startDelay;
translationY = o.translationY;
translationZ = o.translationZ;
scale = o.scale;
@@ -47,6 +49,7 @@
/** Resets the current transform */
public void reset() {
+ startDelay = 0;
translationY = 0;
translationZ = 0;
scale = 1f;
@@ -76,8 +79,8 @@
@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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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.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 @@
/** 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 @@
}
}
+ 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 @@
}
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 @@
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 @@
(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 @@
(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.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.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 @@
// 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 @@
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..3ce19c1f 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 @@
*/
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 @@
} 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 @@
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.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.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 @@
// 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 @@
"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.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 @@
}
/*
- * 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 @@
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 @@
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 @@
}
}
- 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 @@
forwardingResolveInfo.preferredOrder = 0;
forwardingResolveInfo.match = 0;
forwardingResolveInfo.isDefault = true;
- forwardingResolveInfo.filter = cpif;
+ forwardingResolveInfo.filter = filter;
return forwardingResolveInfo;
}
@@ -4199,14 +4246,18 @@
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 @@
}
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 @@
}
}
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 @@
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 @@
return PackageManager.INSTALL_SUCCEEDED;
}
- };
+ }
static String getAsecPackageName(String packageCid) {
int idx = packageCid.lastIndexOf("-");
@@ -10206,13 +10250,13 @@
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 @@
}
@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 @@
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 @@
}
// 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 @@
});
}
- 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 @@
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 @@
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 @@
}
}
}
+ 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 @@
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 @@
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 @@
return configs;
}
-
// static
void JTvInputHal::notify(
tv_input_device_t* dev, tv_input_event_t* event, void* data) {
@@ -232,11 +243,36 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 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 @@
/** @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 @@
*/
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 @@
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 @@
* @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 @@
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 @@
<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 @@
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.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 @@
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 @@
EditText mDeadlineEditText;
RadioButton mWiFiConnectivityRadioButton;
RadioButton mAnyConnectivityRadioButton;
+ CheckBox mRequiresChargingCheckBox;
+ CheckBox mRequiresIdleCheckbox;
+
ComponentName mServiceComponent;
/** Service object to interact scheduled jobs. */
TestJobService mTestService;
@@ -124,24 +131,32 @@
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
new file mode 100644
index 0000000..dc8c197
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg
Binary files differ
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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)