Merge "Making more ContentProviders work across users."
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 b8490c4..ddde2f7 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
@@ -894,7 +895,7 @@
field public static final int permissionFlags = 16843719; // 0x10103c7
field public static final int permissionGroup = 16842762; // 0x101000a
field public static final int permissionGroupFlags = 16843717; // 0x10103c5
- field public static final int persistable = 16843823; // 0x101042f
+ field public static final int persistableMode = 16843823; // 0x101042f
field public static final int persistent = 16842765; // 0x101000d
field public static final int persistentDrawingCache = 16842990; // 0x10100ee
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
@@ -936,6 +937,8 @@
field public static final int progressTintMode = 16843878; // 0x1010466
field public static final int prompt = 16843131; // 0x101017b
field public static final int propertyName = 16843489; // 0x10102e1
+ field public static final int propertyXName = 16843894; // 0x1010476
+ field public static final int propertyYName = 16843895; // 0x1010477
field public static final int protectionLevel = 16842761; // 0x1010009
field public static final int publicKey = 16843686; // 0x10103a6
field public static final int queryActionMsg = 16843227; // 0x10101db
@@ -954,6 +957,7 @@
field public static final int ratingBarStyleIndicator = 16843280; // 0x1010210
field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
field public static final int readPermission = 16842759; // 0x1010007
+ field public static final int relinquishTaskIdentity = 16843896; // 0x1010478
field public static final int repeatCount = 16843199; // 0x10101bf
field public static final int repeatMode = 16843200; // 0x10101c0
field public static final int reqFiveWayNav = 16843314; // 0x1010232
@@ -5194,6 +5198,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 +5208,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 +5216,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 +5225,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,12 +5252,15 @@
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);
method public boolean isDeviceOwnerApp(java.lang.String);
- method public boolean isLockTaskPermitted(android.content.ComponentName);
+ method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isProfileOwnerApp(java.lang.String);
method public void lockNow();
@@ -5263,7 +5274,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
- method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException;
+ method public void setLockTaskPackages(java.lang.String[]) throws java.lang.SecurityException;
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -5282,6 +5293,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";
@@ -8062,6 +8074,7 @@
field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1
field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
+ field public static final int DO_NOT_PERSIST = 1; // 0x1
field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
@@ -8073,13 +8086,15 @@
field public static final int FLAG_IMMERSIVE = 2048; // 0x800
field public static final int FLAG_MULTIPROCESS = 1; // 0x1
field public static final int FLAG_NO_HISTORY = 128; // 0x80
- field public static final int FLAG_PERSISTABLE = 4096; // 0x1000
+ field public static final int FLAG_RELINQUISH_TASK_IDENTITY = 4096; // 0x1000
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
field public static final int LAUNCH_MULTIPLE = 0; // 0x0
field public static final int LAUNCH_SINGLE_INSTANCE = 3; // 0x3
field public static final int LAUNCH_SINGLE_TASK = 2; // 0x2
field public static final int LAUNCH_SINGLE_TOP = 1; // 0x1
+ field public static final int PERSIST_ACROSS_REBOOTS = 2; // 0x2
+ field public static final int PERSIST_ROOT_ONLY = 0; // 0x0
field public static final int SCREEN_ORIENTATION_BEHIND = 3; // 0x3
field public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10; // 0xa
field public static final int SCREEN_ORIENTATION_FULL_USER = 13; // 0xd
@@ -8104,6 +8119,7 @@
field public int maxRecents;
field public java.lang.String parentActivityName;
field public java.lang.String permission;
+ field public int persistableMode;
field public int screenOrientation;
field public int softInputMode;
field public java.lang.String targetActivity;
@@ -8125,6 +8141,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
@@ -8262,15 +8279,26 @@
}
public class LauncherApps {
+ method public void addOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
method public void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public boolean isActivityEnabledForProfile(android.content.ComponentName, android.os.UserHandle);
method public boolean isPackageEnabledForProfile(java.lang.String, android.os.UserHandle);
+ method public void removeOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
method public void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
method public void startActivityForProfile(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
}
+ public static abstract class LauncherApps.OnAppsChangedCallback {
+ ctor public LauncherApps.OnAppsChangedCallback();
+ method public abstract void onPackageAdded(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackageChanged(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
+ method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
+ method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
+ }
+
public static abstract interface LauncherApps.OnAppsChangedListener {
method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String);
method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String);
@@ -12808,6 +12836,7 @@
method public boolean isOutputSupportedFor(int);
method public static boolean isOutputSupportedFor(java.lang.Class<T>);
method public boolean isOutputSupportedFor(android.view.Surface);
+ field public static final long NO_MIN_FRAME_DURATION = 0L; // 0x0L
}
public final class TonemapCurve {
@@ -13715,7 +13744,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
@@ -13789,6 +13820,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";
@@ -15833,6 +15865,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";
@@ -15846,6 +15879,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;
@@ -15877,6 +15911,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 {
@@ -15897,6 +15947,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;
@@ -15905,17 +15957,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 {
@@ -21546,6 +21598,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);
@@ -21563,7 +21616,7 @@
method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
- field public static final java.lang.String DISALLOW_CONFIG_APPS = "no_config_apps";
+ field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
@@ -23410,7 +23463,7 @@
field public static final java.lang.String CONTENT_DIRECTORY = "data";
}
- public static final class ContactsContract.Contacts.Entity implements android.provider.BaseColumns android.provider.ContactsContract.BaseSyncColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns android.provider.ContactsContract.SyncColumns {
+ public static final class ContactsContract.Contacts.Entity implements android.provider.BaseColumns android.provider.ContactsContract.BaseSyncColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.DataUsageStatColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns android.provider.ContactsContract.SyncColumns {
field public static final java.lang.String CONTENT_DIRECTORY = "entities";
field public static final java.lang.String DATA_ID = "data_id";
field public static final java.lang.String RAW_CONTACT_ID = "raw_contact_id";
@@ -31304,6 +31357,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();
@@ -31312,6 +31366,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);
@@ -32544,6 +32599,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/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 92c6a51..4e91361 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -17,12 +17,19 @@
package com.android.commands.media;
-import android.app.PendingIntent;
+import android.app.ActivityManager;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.media.IAudioService;
-import android.media.IRemoteControlDisplay;
+import android.media.MediaMetadata;
+import android.media.session.ISessionController;
+import android.media.session.ISessionManager;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionInfo;
+import android.media.session.PlaybackState;
+import android.media.session.RouteInfo;
import android.os.Bundle;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -30,16 +37,17 @@
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+
import com.android.internal.os.BaseCommand;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
+import java.util.List;
public class Media extends BaseCommand {
-
- private IAudioService mAudioService;
+ private ISessionManager mSessionService;
/**
* Command-line entry point.
@@ -54,29 +62,35 @@
out.println(
"usage: media [subcommand] [options]\n" +
" media dispatch KEY\n" +
- " media remote-display\n" +
+ " media list-sessions\n" +
+ " media monitor <sessionId>\n" +
"\n" +
- "media dispatch: dispatch a media key to the current media client.\n" +
+ "media dispatch: dispatch a media key to the system.\n" +
" KEY may be: play, pause, play-pause, mute, headsethook,\n" +
- " stop, next, previous, rewind, recordm fast-forword.\n" +
- "media remote-display: monitor remote display updates.\n"
+ " stop, next, previous, rewind, record, fast-forword.\n" +
+ "media list-sessions: print a list of the current sessions.\n" +
+ "media monitor: monitor updates to the specified session.\n" +
+ " Use the sessionId from list-sessions.\n"
);
}
public void onRun() throws Exception {
- mAudioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
- Context.AUDIO_SERVICE));
- if (mAudioService == null) {
+ mSessionService = ISessionManager.Stub.asInterface(ServiceManager.checkService(
+ Context.MEDIA_SESSION_SERVICE));
+ if (mSessionService == null) {
System.err.println(NO_SYSTEM_ERROR_CODE);
- throw new AndroidException("Can't connect to audio service; is the system running?");
+ throw new AndroidException(
+ "Can't connect to media session service; is the system running?");
}
String op = nextArgRequired();
if (op.equals("dispatch")) {
runDispatch();
- } else if (op.equals("remote-display")) {
- runRemoteDisplay();
+ } else if (op.equals("list-sessions")) {
+ runListSessions();
+ } else if (op.equals("monitor")) {
+ runMonitor();
} else {
showError("Error: unknown command '" + op + "'");
return;
@@ -85,11 +99,39 @@
private void sendMediaKey(KeyEvent event) {
try {
- mAudioService.dispatchMediaKeyEvent(event);
+ mSessionService.dispatchMediaKeyEvent(event, false);
} catch (RemoteException e) {
}
}
+ private void runMonitor() throws Exception {
+ String id = nextArgRequired();
+ if (id == null) {
+ showError("Error: must include a session id");
+ return;
+ }
+ boolean success = false;
+ try {
+ List<IBinder> sessions = mSessionService
+ .getSessions(null, ActivityManager.getCurrentUser());
+ for (IBinder session : sessions) {
+ MediaController controller = MediaController.fromBinder(ISessionController.Stub
+ .asInterface(session));
+ if (controller != null && controller.getSessionInfo().getId().equals(id)) {
+ ControllerMonitor monitor = new ControllerMonitor(controller);
+ monitor.run();
+ success = true;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("***Error monitoring session*** " + e.getMessage());
+ }
+ if (!success) {
+ System.out.println("No session found with id " + id);
+ }
+ }
+
private void runDispatch() throws Exception {
String cmd = nextArgRequired();
int keycode;
@@ -127,65 +169,49 @@
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
}
- class RemoteDisplayMonitor extends IRemoteControlDisplay.Stub {
- RemoteDisplayMonitor() {
+ class ControllerMonitor extends MediaController.Callback {
+ private final MediaController mController;
+
+ public ControllerMonitor(MediaController controller) {
+ mController = controller;
}
-
-
@Override
- public void setCurrentClientId(int clientGeneration, PendingIntent clientMediaIntent,
- boolean clearing) {
- System.out.println("New client: id=" + clientGeneration
- + " intent=" + clientMediaIntent + " clearing=" + clearing);
+ public void onSessionEvent(String event, Bundle extras) {
+ System.out.println("onSessionEvent event=" + event + ", extras=" + extras);
}
@Override
- public void setEnabled(boolean enabled) {
- System.out.println("New enable state= " + (enabled ? "enabled" : "disabled"));
+ public void onRouteChanged(RouteInfo route) {
+ System.out.println("onRouteChanged " + route);
}
@Override
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
- long currentPosMs, float speed) {
- System.out.println("New state: id=" + generationId + " state=" + state
- + " time=" + stateChangeTimeMs + " pos=" + currentPosMs + " speed=" + speed);
+ public void onPlaybackStateChanged(PlaybackState state) {
+ System.out.println("onPlaybackStateChanged " + state);
}
@Override
- public void setTransportControlInfo(int generationId, int transportControlFlags,
- int posCapabilities) {
- System.out.println("New control info: id=" + generationId
- + " flags=0x" + Integer.toHexString(transportControlFlags)
- + " cap=0x" + Integer.toHexString(posCapabilities));
- }
-
- @Override
- public void setMetadata(int generationId, Bundle metadata) {
- System.out.println("New metadata: id=" + generationId
- + " data=" + metadata);
- }
-
- @Override
- public void setArtwork(int generationId, Bitmap artwork) {
- System.out.println("New artwork: id=" + generationId
- + " art=" + artwork);
- }
-
- @Override
- public void setAllMetadata(int generationId, Bundle metadata, Bitmap artwork) {
- System.out.println("New metadata+artwork: id=" + generationId
- + " data=" + metadata + " art=" + artwork);
+ public void onMetadataChanged(MediaMetadata metadata) {
+ String mmString = metadata == null ? null : "title=" + metadata
+ .getString(MediaMetadata.METADATA_KEY_TITLE);
+ System.out.println("onMetadataChanged " + mmString);
}
void printUsageMessage() {
- System.out.println("Monitoring remote control displays... available commands:");
+ System.out.println("V2Monitoring session " + mController.getSessionInfo().getId()
+ + "... available commands:");
System.out.println("(q)uit: finish monitoring");
}
void run() throws RemoteException {
printUsageMessage();
-
- mAudioService.registerRemoteControlDisplay(this, 0, 0);
+ HandlerThread cbThread = new HandlerThread("MediaCb") {
+ @Override
+ protected void onLooperPrepared() {
+ mController.addCallback(ControllerMonitor.this);
+ }
+ };
+ cbThread.start();
try {
InputStreamReader converter = new InputStreamReader(System.in);
@@ -209,17 +235,35 @@
printUsageMessage();
}
}
-
} catch (IOException e) {
e.printStackTrace();
} finally {
- mAudioService.unregisterRemoteControlDisplay(this);
+ cbThread.getLooper().quit();
+ try {
+ mController.removeCallback(this);
+ } catch (Exception e) {
+ // ignoring
+ }
}
}
}
- private void runRemoteDisplay() throws Exception {
- RemoteDisplayMonitor monitor = new RemoteDisplayMonitor();
- monitor.run();
+ private void runListSessions() {
+ System.out.println("Sessions:");
+ try {
+ List<IBinder> sessions = mSessionService
+ .getSessions(null, ActivityManager.getCurrentUser());
+ for (IBinder session : sessions) {
+ MediaController controller = MediaController.fromBinder(ISessionController.Stub
+ .asInterface(session));
+ if (controller != null) {
+ MediaSessionInfo info = controller.getSessionInfo();
+ System.out.println(" id=" + info.getId() + ", package="
+ + info.getPackageName());
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("***Error listing sessions***");
+ }
}
}
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 06f5aca..39fcf73 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -17,11 +17,13 @@
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.content.res.Resources.NotFoundException;
+import android.graphics.Path;
import android.util.AttributeSet;
+import android.util.PathParser;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
@@ -158,7 +160,7 @@
int stateIndex = 0;
for (int i = 0; i < attributeCount; i++) {
int attrName = attributeSet.getAttributeNameResource(i);
- if (attrName == com.android.internal.R.attr.animation) {
+ if (attrName == R.attr.animation) {
animator = loadAnimator(context,
attributeSet.getAttributeResourceValue(i, 0));
} else {
@@ -186,36 +188,43 @@
}
}
+ /**
+ * @param anim Null if this is a ValueAnimator, otherwise this is an
+ * ObjectAnimator
+ * @param arrayAnimator Incoming typed array for Animator's attributes.
+ * @param arrayObjectAnimator Incoming typed array for Object Animator's
+ * attributes.
+ */
+ private static void parseAnimatorFromTypeArray(ValueAnimator anim,
+ TypedArray arrayAnimator, TypedArray arrayObjectAnimator) {
+ long duration = arrayAnimator.getInt(R.styleable.Animator_duration, 300);
- private static void parseAnimatorFromTypeArray(ValueAnimator anim, TypedArray a) {
- long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 300);
+ long startDelay = arrayAnimator.getInt(R.styleable.Animator_startOffset, 0);
- long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
-
- int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
+ int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType,
VALUE_TYPE_FLOAT);
if (anim == null) {
anim = new ValueAnimator();
}
- TypeEvaluator evaluator = null;
- int valueFromIndex = com.android.internal.R.styleable.Animator_valueFrom;
- int valueToIndex = com.android.internal.R.styleable.Animator_valueTo;
+ TypeEvaluator evaluator = null;
+ int valueFromIndex = R.styleable.Animator_valueFrom;
+ int valueToIndex = R.styleable.Animator_valueTo;
boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
- TypedValue tvFrom = a.peekValue(valueFromIndex);
+ TypedValue tvFrom = arrayAnimator.peekValue(valueFromIndex);
boolean hasFrom = (tvFrom != null);
int fromType = hasFrom ? tvFrom.type : 0;
- TypedValue tvTo = a.peekValue(valueToIndex);
+ TypedValue tvTo = arrayAnimator.peekValue(valueToIndex);
boolean hasTo = (tvTo != null);
int toType = hasTo ? tvTo.type : 0;
if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(fromType <= TypedValue.TYPE_LAST_COLOR_INT)) ||
- (hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
- (toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
+ (hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
+ (toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
// special case for colors: ignore valueType and get ints
getFloats = false;
evaluator = ArgbEvaluator.getInstance();
@@ -226,15 +235,15 @@
float valueTo;
if (hasFrom) {
if (fromType == TypedValue.TYPE_DIMENSION) {
- valueFrom = a.getDimension(valueFromIndex, 0f);
+ valueFrom = arrayAnimator.getDimension(valueFromIndex, 0f);
} else {
- valueFrom = a.getFloat(valueFromIndex, 0f);
+ valueFrom = arrayAnimator.getFloat(valueFromIndex, 0f);
}
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
- valueTo = a.getDimension(valueToIndex, 0f);
+ valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
} else {
- valueTo = a.getFloat(valueToIndex, 0f);
+ valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
}
anim.setFloatValues(valueFrom, valueTo);
} else {
@@ -242,9 +251,9 @@
}
} else {
if (toType == TypedValue.TYPE_DIMENSION) {
- valueTo = a.getDimension(valueToIndex, 0f);
+ valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
} else {
- valueTo = a.getFloat(valueToIndex, 0f);
+ valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
}
anim.setFloatValues(valueTo);
}
@@ -253,21 +262,21 @@
int valueTo;
if (hasFrom) {
if (fromType == TypedValue.TYPE_DIMENSION) {
- valueFrom = (int) a.getDimension(valueFromIndex, 0f);
+ valueFrom = (int) arrayAnimator.getDimension(valueFromIndex, 0f);
} else if ((fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(fromType <= TypedValue.TYPE_LAST_COLOR_INT)) {
- valueFrom = a.getColor(valueFromIndex, 0);
+ valueFrom = arrayAnimator.getColor(valueFromIndex, 0);
} else {
- valueFrom = a.getInt(valueFromIndex, 0);
+ valueFrom = arrayAnimator.getInt(valueFromIndex, 0);
}
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
- valueTo = (int) a.getDimension(valueToIndex, 0f);
+ valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
} else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
- valueTo = a.getColor(valueToIndex, 0);
+ valueTo = arrayAnimator.getColor(valueToIndex, 0);
} else {
- valueTo = a.getInt(valueToIndex, 0);
+ valueTo = arrayAnimator.getInt(valueToIndex, 0);
}
anim.setIntValues(valueFrom, valueTo);
} else {
@@ -276,12 +285,12 @@
} else {
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
- valueTo = (int) a.getDimension(valueToIndex, 0f);
+ valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
} else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
- (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
- valueTo = a.getColor(valueToIndex, 0);
+ (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
+ valueTo = arrayAnimator.getColor(valueToIndex, 0);
} else {
- valueTo = a.getInt(valueToIndex, 0);
+ valueTo = arrayAnimator.getInt(valueToIndex, 0);
}
anim.setIntValues(valueTo);
}
@@ -291,18 +300,59 @@
anim.setDuration(duration);
anim.setStartDelay(startDelay);
- if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) {
+ if (arrayAnimator.hasValue(R.styleable.Animator_repeatCount)) {
anim.setRepeatCount(
- a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0));
+ arrayAnimator.getInt(R.styleable.Animator_repeatCount, 0));
}
- if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
+ if (arrayAnimator.hasValue(R.styleable.Animator_repeatMode)) {
anim.setRepeatMode(
- a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
+ arrayAnimator.getInt(R.styleable.Animator_repeatMode,
ValueAnimator.RESTART));
}
if (evaluator != null) {
anim.setEvaluator(evaluator);
}
+
+ if (arrayObjectAnimator != null) {
+ ObjectAnimator oa = (ObjectAnimator) anim;
+ String pathData = arrayObjectAnimator.getString(R.styleable.PropertyAnimator_pathData);
+
+ // Note that if there is a pathData defined in the Object Animator,
+ // valueFrom / valueTo will be overwritten by the pathData.
+ if (pathData != null) {
+ String propertyXName =
+ arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyXName);
+ String propertyYName =
+ arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyYName);
+
+ if (propertyXName == null && propertyYName == null) {
+ throw new IllegalArgumentException("propertyXName or propertyYName"
+ + " is needed for PathData in Object Animator");
+ } else {
+ Path path = PathParser.createPathFromPathData(pathData);
+ Keyframe[][] keyframes = PropertyValuesHolder.createKeyframes(path, !getFloats);
+ PropertyValuesHolder x = null;
+ PropertyValuesHolder y = null;
+ if (propertyXName != null) {
+ x = PropertyValuesHolder.ofKeyframe(propertyXName, keyframes[0]);
+ }
+ if (propertyYName != null) {
+ y = PropertyValuesHolder.ofKeyframe(propertyYName, keyframes[1]);
+ }
+ if (x == null) {
+ oa.setValues(y);
+ } else if (y == null) {
+ oa.setValues(x);
+ } else {
+ oa.setValues(x, y);
+ }
+ }
+ } else {
+ String propertyName =
+ arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyName);
+ oa.setPropertyName(propertyName);
+ }
+ }
}
private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser)
@@ -338,11 +388,11 @@
anim = new AnimatorSet();
TypedArray a;
if (theme != null) {
- a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimatorSet, 0, 0);
+ a = theme.obtainStyledAttributes(attrs, R.styleable.AnimatorSet, 0, 0);
} else {
- a = res.obtainAttributes(attrs, com.android.internal.R.styleable.AnimatorSet);
+ a = res.obtainAttributes(attrs, R.styleable.AnimatorSet);
}
- int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
+ int ordering = a.getInt(R.styleable.AnimatorSet_ordering,
TOGETHER);
createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering);
a.recycle();
@@ -380,19 +430,6 @@
loadAnimator(res, theme, attrs, anim);
- TypedArray a;
- if (theme != null) {
- a = theme.obtainStyledAttributes(attrs, R.styleable.PropertyAnimator, 0, 0);
- } else {
- a = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
- }
-
- String propertyName = a.getString(R.styleable.PropertyAnimator_propertyName);
-
- anim.setPropertyName(propertyName);
-
- a.recycle();
-
return anim;
}
@@ -402,26 +439,41 @@
*
* @param res The resources
* @param attrs The set of attributes holding the animation parameters
+ * @param anim Null if this is a ValueAnimator, otherwise this is an
+ * ObjectAnimator
*/
private static ValueAnimator loadAnimator(Resources res, Theme theme,
AttributeSet attrs, ValueAnimator anim)
throws NotFoundException {
- TypedArray a;
+ TypedArray arrayAnimator = null;
+ TypedArray arrayObjectAnimator = null;
+
if (theme != null) {
- a = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0);
+ arrayAnimator = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0);
} else {
- a = res.obtainAttributes(attrs, R.styleable.Animator);
+ arrayAnimator = res.obtainAttributes(attrs, R.styleable.Animator);
}
- parseAnimatorFromTypeArray(anim, a);
+ // If anim is not null, then it is an object animator.
+ if (anim != null) {
+ if (theme != null) {
+ arrayObjectAnimator = theme.obtainStyledAttributes(attrs,
+ R.styleable.PropertyAnimator, 0, 0);
+ } else {
+ arrayObjectAnimator = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
+ }
+ }
+ parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator);
final int resID =
- a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+ arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0);
if (resID > 0) {
anim.setInterpolator(AnimationUtils.loadInterpolator(res, theme, resID));
}
- a.recycle();
+
+ arrayAnimator.recycle();
+ arrayObjectAnimator.recycle();
return anim;
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f6883e2..9132883 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -929,7 +929,8 @@
/**
* Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
- * the attribute {@link android.R.attr#persistable} set true.
+ * the attribute {@link android.R.attr#persistableMode} set to
+ * <code>persistAcrossReboots</code>.
*
* @param savedInstanceState if the activity is being re-initialized after
* previously being shut down then this Bundle contains the data it most
@@ -1012,8 +1013,9 @@
/**
* This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities
- * created with the attribute {@link android.R.attr#persistable}. The {@link
- * android.os.PersistableBundle} passed came from the restored PersistableBundle first
+ * created with the attribute {@link android.R.attr#persistableMode} set to
+ * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+ * came from the restored PersistableBundle first
* saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
*
* <p>This method is called between {@link #onStart} and
@@ -1111,7 +1113,8 @@
/**
* This is the same as {@link #onPostCreate(Bundle)} but is called for activities
- * created with the attribute {@link android.R.attr#persistable}.
+ * created with the attribute {@link android.R.attr#persistableMode} set to
+ * <code>persistAcrossReboots</code>.
*
* @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState}
* @param persistentState The data caming from the PersistableBundle first
@@ -1352,10 +1355,10 @@
/**
* This is the same as {@link #onSaveInstanceState} but is called for activities
- * created with the attribute {@link android.R.attr#persistable}. The {@link
- * android.os.PersistableBundle} passed in will be saved and presented in
- * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity
- * is restarted following the next device reboot.
+ * created with the attribute {@link android.R.attr#persistableMode} set to
+ * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+ * in will be saved and presented in {@link #onCreate(Bundle, PersistableBundle)}
+ * the first time that this activity is restarted following the next device reboot.
*
* @param outState Bundle in which to place your saved state.
* @param outPersistentState State which will be saved across reboots.
@@ -5320,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 {
@@ -5343,6 +5348,7 @@
// Window is already translucent.
mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
}
+ return mChangeCanvasToTranslucent;
}
/** @hide */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b8f2089..f5514f8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -319,7 +319,7 @@
}
public boolean isPersistable() {
- return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+ return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
}
public String toString() {
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 e80c761..df6be8b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -1506,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);
}
@@ -1521,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);
}
@@ -1536,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();
@@ -1547,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);
}
@@ -1566,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.
@@ -1839,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");
}
@@ -2340,15 +2344,20 @@
}
/**
- * Sets which components may enter lock task mode.
+ * Sets which packages may enter lock task mode.
+ *
+ * <p>Any packages that shares uid with an allowed package will also be allowed
+ * to activate lock task.
*
* This function can only be called by the device owner or the profile owner.
- * @param components The list of components allowed to enter lock task mode
+ * @param packages The list of packages allowed to enter lock task mode
+ *
+ * @see Activity#startLockTask()
*/
- public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+ public void setLockTaskPackages(String[] packages) throws SecurityException {
if (mService != null) {
try {
- mService.setLockTaskComponents(components);
+ mService.setLockTaskPackages(packages);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2356,13 +2365,13 @@
}
/**
- * This function returns the list of components allowed to start the lock task mode.
+ * This function returns the list of packages allowed to start the lock task mode.
* @hide
*/
- public ComponentName[] getLockTaskComponents() {
+ public String[] getLockTaskPackages() {
if (mService != null) {
try {
- return mService.getLockTaskComponents();
+ return mService.getLockTaskPackages();
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2373,12 +2382,12 @@
/**
* This function lets the caller know whether the given component is allowed to start the
* lock task mode.
- * @param component The component to check
+ * @param pkg The package to check
*/
- public boolean isLockTaskPermitted(ComponentName component) {
+ public boolean isLockTaskPermitted(String pkg) {
if (mService != null) {
try {
- return mService.isLockTaskPermitted(component);
+ return mService.isLockTaskPermitted(pkg);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a1caa21..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);
@@ -142,13 +142,15 @@
void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
String[] getAccountTypesWithManagementDisabled();
- void setLockTaskComponents(in ComponentName[] components);
- ComponentName[] getLockTaskComponents();
- boolean isLockTaskPermitted(in ComponentName component);
+ void setLockTaskPackages(in String[] packages);
+ String[] getLockTaskPackages();
+ boolean isLockTaskPermitted(in String pkg);
void setGlobalSetting(in ComponentName who, in String setting, in String value);
void setSecureSetting(in ComponentName who, in String setting, in String value);
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/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index 706ef04..4631323 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -31,12 +31,22 @@
* @hide
*/
public class BackupTransport {
+ // Zero return always means things are okay. If returned from
+ // getNextFullRestoreDataChunk(), it means that no data could be delivered at
+ // this time, but the restore is still running and the caller should simply
+ // retry.
public static final int TRANSPORT_OK = 0;
- public static final int TRANSPORT_ERROR = 1;
- public static final int TRANSPORT_NOT_INITIALIZED = 2;
- public static final int TRANSPORT_PACKAGE_REJECTED = 3;
- public static final int AGENT_ERROR = 4;
- public static final int AGENT_UNKNOWN = 5;
+
+ // -1 is special; it is used in getNextFullRestoreDataChunk() to indicate that
+ // we've delivered the entire data stream for the current restore target.
+ public static final int NO_MORE_DATA = -1;
+
+ // Result codes that indicate real errors are negative and not -1
+ public static final int TRANSPORT_ERROR = -1000;
+ public static final int TRANSPORT_NOT_INITIALIZED = -1001;
+ public static final int TRANSPORT_PACKAGE_REJECTED = -1002;
+ public static final int AGENT_ERROR = -1003;
+ public static final int AGENT_UNKNOWN = -1004;
IBackupTransport mBinderImpl = new TransportImpl();
/** @hide */
@@ -370,11 +380,14 @@
* @param socket The file descriptor that the transport will use for delivering the
* streamed archive. The transport must close this socket in all cases when returning
* from this method.
- * @return 0 when no more data for the current package is available. A positive value
- * indicates the presence of that many bytes to be delivered to the app. Any negative
- * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
- * indicating a fatal error condition that precludes further restore operations
- * on the current dataset.
+ * @return {@link #NO_MORE_DATA} when no more data for the current package is available.
+ * A positive value indicates the presence of that many bytes to be delivered to the app.
+ * A value of zero indicates that no data was deliverable at this time, but the restore
+ * is still running and the caller should retry. {@link #TRANSPORT_PACKAGE_REJECTED}
+ * means that the current package's restore operation should be aborted, but that
+ * the transport itself is still in a good state and so a multiple-package restore
+ * sequence can still be continued. Any other negative return value is treated as a
+ * fatal error condition that aborts all further restore operations on the current dataset.
*/
public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
return 0;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6e53a6fb..3dfa78b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7375,6 +7375,7 @@
for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) {
out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx));
}
+ out.endTag(null, TAG_CATEGORIES);
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 791e5aa..bcf1e87 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -104,6 +104,28 @@
public int documentLaunchMode;
/**
+ * Constant corresponding to <code>persistRootOnly</code> in
+ * the {@link android.R.attr#persistableMode} attribute.
+ */
+ public static final int PERSIST_ROOT_ONLY = 0;
+ /**
+ * Constant corresponding to <code>doNotPersist</code> in
+ * the {@link android.R.attr#persistableMode} attribute.
+ */
+ public static final int DO_NOT_PERSIST = 1;
+ /**
+ * Constant corresponding to <code>persistAcrossReboots</code> in
+ * the {@link android.R.attr#persistableMode} attribute.
+ */
+ public static final int PERSIST_ACROSS_REBOOTS = 2;
+ /**
+ * Value indicating how this activity is to be persisted across
+ * reboots for restoring in the Recents list.
+ * {@link android.R.attr#persistableMode}
+ */
+ public int persistableMode;
+
+ /**
* The maximum number of tasks rooted at this activity that can be in the recent task list.
* Refer to {@link android.R.attr#maxRecents}.
*/
@@ -230,11 +252,12 @@
*/
public static final int FLAG_IMMERSIVE = 0x0800;
/**
- * Bit in {@link #flags} indicating that this activity is to be persisted across
- * reboots for display in the Recents list.
- * {@link android.R.attr#persistable}
+ * Bit in {@link #flags}: If set, a task rooted at this activity will have its
+ * baseIntent replaced by the activity immediately above this. Each activity may further
+ * relinquish its identity to the activity above it using this flag. Set from the
+ * android.R.attr#relinquishTaskIdentity attribute.
*/
- public static final int FLAG_PERSISTABLE = 0x1000;
+ public static final int FLAG_RELINQUISH_TASK_IDENTITY = 0x1000;
/**
* Bit in {@link #flags} indicating that tasks started with this activity are to be
* removed from the recent list of tasks when the last activity in the task is finished.
@@ -641,13 +664,23 @@
return theme != 0 ? theme : applicationInfo.theme;
}
+ private String persistableModeToString() {
+ switch(persistableMode) {
+ case PERSIST_ROOT_ONLY: return "PERSIST_ROOT_ONLY";
+ case DO_NOT_PERSIST: return "DO_NOT_PERSIST";
+ case PERSIST_ACROSS_REBOOTS: return "PERSIST_ACROSS_REBOOTS";
+ default: return "UNKNOWN=" + persistableMode;
+ }
+ }
+
public void dump(Printer pw, String prefix) {
super.dumpFront(pw, prefix);
if (permission != null) {
pw.println(prefix + "permission=" + permission);
}
pw.println(prefix + "taskAffinity=" + taskAffinity
- + " targetActivity=" + targetActivity);
+ + " targetActivity=" + targetActivity
+ + " persistableMode=" + persistableModeToString());
if (launchMode != 0 || flags != 0 || theme != 0) {
pw.println(prefix + "launchMode=" + launchMode
+ " flags=0x" + Integer.toHexString(flags)
@@ -688,6 +721,7 @@
dest.writeInt(softInputMode);
dest.writeInt(uiOptions);
dest.writeString(parentActivityName);
+ dest.writeInt(persistableMode);
}
public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -713,5 +747,6 @@
softInputMode = source.readInt();
uiOptions = source.readInt();
parentActivityName = source.readString();
+ persistableMode = source.readInt();
}
}
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/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 69fa408..6c10bb8 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -57,6 +57,65 @@
private List<OnAppsChangedListener> mListeners
= new ArrayList<OnAppsChangedListener>();
+ private List<OnAppsChangedCallback> mCallbacks
+ = new ArrayList<OnAppsChangedCallback>();
+
+ /**
+ * Callbacks for package changes to this and related managed profiles.
+ */
+ public static abstract class OnAppsChangedCallback {
+ /**
+ * Indicates that a package was removed from the specified profile.
+ *
+ * @param packageName The name of the package that was removed.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ abstract public void onPackageRemoved(String packageName, UserHandle user);
+
+ /**
+ * Indicates that a package was added to the specified profile.
+ *
+ * @param packageName The name of the package that was added.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ abstract public void onPackageAdded(String packageName, UserHandle user);
+
+ /**
+ * Indicates that a package was modified in the specified profile.
+ *
+ * @param packageName The name of the package that has changed.
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ abstract public void onPackageChanged(String packageName, UserHandle user);
+
+ /**
+ * Indicates that one or more packages have become available. For
+ * example, this can happen when a removable storage card has
+ * reappeared.
+ *
+ * @param packageNames The names of the packages that have become
+ * available.
+ * @param user The UserHandle of the profile that generated the change.
+ * @param replacing Indicates whether these packages are replacing
+ * existing ones.
+ */
+ abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
+ boolean replacing);
+
+ /**
+ * Indicates that one or more packages have become unavailable. For
+ * example, this can happen when a removable storage card has been
+ * removed.
+ *
+ * @param packageNames The names of the packages that have become
+ * unavailable.
+ * @param user The UserHandle of the profile that generated the change.
+ * @param replacing Indicates whether the packages are about to be
+ * replaced with new versions.
+ */
+ abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+ boolean replacing);
+ }
/**
* Callbacks for package changes to this and related managed profiles.
@@ -270,7 +329,7 @@
synchronized (this) {
if (listener != null && !mListeners.contains(listener)) {
mListeners.add(listener);
- if (mListeners.size() == 1) {
+ if (mListeners.size() == 1 && mCallbacks.size() == 0) {
try {
mService.addOnAppsChangedListener(mAppsChangedListener);
} catch (RemoteException re) {
@@ -289,7 +348,44 @@
public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
synchronized (this) {
mListeners.remove(listener);
- if (mListeners.size() == 0) {
+ if (mListeners.size() == 0 && mCallbacks.size() == 0) {
+ try {
+ mService.removeOnAppsChangedListener(mAppsChangedListener);
+ } catch (RemoteException re) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a callback for changes to packages in current and managed profiles.
+ *
+ * @param callback The callback to add.
+ */
+ public void addOnAppsChangedCallback(OnAppsChangedCallback callback) {
+ synchronized (this) {
+ if (callback != null && !mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ if (mCallbacks.size() == 1 && mListeners.size() == 0) {
+ try {
+ mService.addOnAppsChangedListener(mAppsChangedListener);
+ } catch (RemoteException re) {
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a callback that was previously added.
+ *
+ * @param callback The callback to remove.
+ * @see #addOnAppsChangedListener(OnAppsChangedCallback)
+ */
+ public void removeOnAppsChangedCallback(OnAppsChangedCallback callback) {
+ synchronized (this) {
+ mListeners.remove(callback);
+ if (mListeners.size() == 0 && mCallbacks.size() == 0) {
try {
mService.removeOnAppsChangedListener(mAppsChangedListener);
} catch (RemoteException re) {
@@ -309,6 +405,9 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackageRemoved(user, packageName);
}
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackageRemoved(packageName, user);
+ }
}
}
@@ -321,6 +420,9 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackageChanged(user, packageName);
}
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackageChanged(packageName, user);
+ }
}
}
@@ -333,6 +435,9 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackageAdded(user, packageName);
}
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackageAdded(packageName, user);
+ }
}
}
@@ -346,6 +451,9 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackagesAvailable(user, packageNames, replacing);
}
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackagesAvailable(packageNames, user, replacing);
+ }
}
}
@@ -359,7 +467,10 @@
for (OnAppsChangedListener listener : mListeners) {
listener.onPackagesUnavailable(user, packageNames, replacing);
}
- }
+ for (OnAppsChangedCallback callback : mCallbacks) {
+ callback.onPackagesUnavailable(packageNames, user, replacing);
+ }
+ }
}
};
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8d9b8d9..9d871c5 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,13 @@
*
*/
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);
+ parser.collectManifestDigest(pkg);
}
PackageUserState state = new PackageUserState();
return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
@@ -3586,30 +3600,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 91895ff..b40a441 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,11 @@
* {@code AndroidManifest.xml}, {@code true} is returned.
*/
public void collectManifestDigest(Package pkg) throws PackageParserException {
+ pkg.manifestDigest = null;
+
+ // 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 +757,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 +889,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 +993,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 +1011,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 +1045,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 +1795,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 +1803,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 +2090,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;
+ }
}
}
@@ -2607,10 +2614,9 @@
com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
0);
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) {
- a.info.flags |= ActivityInfo.FLAG_PERSISTABLE;
- }
+ a.info.persistableMode = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifestActivity_persistableMode,
+ ActivityInfo.PERSIST_ROOT_ONLY);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded,
@@ -2623,6 +2629,12 @@
false)) {
a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
}
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestActivity_relinquishTaskIdentity,
+ false)) {
+ a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+ }
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
@@ -3428,8 +3440,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);
@@ -3672,7 +3683,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.
@@ -3718,7 +3732,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.
@@ -3780,8 +3795,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;
@@ -3789,6 +3804,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;
@@ -4391,6 +4415,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;
@@ -4398,5 +4449,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/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0705e0c..44fc3b6 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -172,6 +172,11 @@
private static final int NO_ERROR = 0;
private static final int EACCESS = -13;
private static final int ENODEV = -19;
+ private static final int EBUSY = -16;
+ private static final int EINVAL = -22;
+ private static final int ENOSYS = -38;
+ private static final int EUSERS = -87;
+ private static final int EOPNOTSUPP = -95;
/**
* Broadcast Action: A new picture is taken by the camera, and the entry of
@@ -190,6 +195,22 @@
public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO";
/**
+ * Camera HAL device API version 1.0
+ * @hide
+ */
+ public static final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
+
+ /**
+ * A constant meaning the normal camera connect/open will be used.
+ * @hide
+ */
+ public static final int CAMERA_HAL_API_VERSION_NORMAL_OPEN = -2;
+
+ /**
+ * Used to indicate HAL version un-specified.
+ */
+ private static final int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1;
+ /**
* Hardware face detection. It does not use much CPU.
*/
private static final int CAMERA_FACE_DETECTION_HW = 0;
@@ -331,6 +352,111 @@
return null;
}
+ /**
+ * Creates a new Camera object to access a particular hardware camera with
+ * given hal API version. If the same camera is opened by other applications
+ * or the hal API version is not supported by this device, this will throw a
+ * RuntimeException.
+ * <p>
+ * You must call {@link #release()} when you are done using the camera,
+ * otherwise it will remain locked and be unavailable to other applications.
+ * <p>
+ * Your application should only have one Camera object active at a time for
+ * a particular hardware camera.
+ * <p>
+ * Callbacks from other methods are delivered to the event loop of the
+ * thread which called open(). If this thread has no event loop, then
+ * callbacks are delivered to the main application event loop. If there is
+ * no main application event loop, callbacks are not delivered.
+ * <p class="caution">
+ * <b>Caution:</b> On some devices, this method may take a long time to
+ * complete. It is best to call this method from a worker thread (possibly
+ * using {@link android.os.AsyncTask}) to avoid blocking the main
+ * application UI thread.
+ *
+ * @param cameraId The hardware camera to access, between 0 and
+ * {@link #getNumberOfCameras()}-1.
+ * @param halVersion The HAL API version this camera device to be opened as. When
+ * it is {@value #CAMERA_HAL_API_VERSION_NORMAL_OPEN}, the methods will be equivalent
+ * to {@link #open}, but more detailed error information will be returned to managed code.
+ * @return a new Camera object, connected, locked and ready for use.
+ * @throws RuntimeException if opening the camera fails (for example, if the
+ * camera is in use by another process or device policy manager has disabled
+ * the camera).
+ * @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
+ *
+ * @hide
+ */
+ public static Camera openLegacy(int cameraId, int halVersion) {
+ return new Camera(cameraId, halVersion);
+ }
+
+ /**
+ * Create a legacy camera object.
+ *
+ * @param cameraId The hardware camera to access, between 0 and
+ * {@link #getNumberOfCameras()}-1.
+ * @param halVersion The HAL API version this camera device to be opened as.
+ */
+ private Camera(int cameraId, int halVersion) {
+ int err = cameraInit(cameraId, halVersion);
+ if (checkInitErrors(err)) {
+ switch(err) {
+ case EACCESS:
+ throw new RuntimeException("Fail to connect to camera service");
+ case ENODEV:
+ throw new RuntimeException("Camera initialization failed");
+ case ENOSYS:
+ throw new RuntimeException("Camera initialization failed because some methods"
+ + " are not implemented");
+ case EOPNOTSUPP:
+ throw new RuntimeException("Camera initialization failed because the hal"
+ + " version is not supported by this device");
+ case EINVAL:
+ throw new RuntimeException("Camera initialization failed because the input"
+ + " arugments are invalid");
+ case EBUSY:
+ throw new RuntimeException("Camera initialization failed because the camera"
+ + " device was already opened");
+ case EUSERS:
+ throw new RuntimeException("Camera initialization failed because the max"
+ + " number of camera devices were already opened");
+ default:
+ // Should never hit this.
+ throw new RuntimeException("Unknown camera error");
+ }
+ }
+ }
+
+ private int cameraInit(int cameraId, int halVersion) {
+ // This function should be only called by Camera(int cameraId, int halVersion).
+ if (halVersion < CAMERA_HAL_API_VERSION_1_0 &&
+ halVersion != CAMERA_HAL_API_VERSION_NORMAL_OPEN) {
+ throw new IllegalArgumentException("Invalid HAL version " + halVersion);
+ }
+
+ mShutterCallback = null;
+ mRawImageCallback = null;
+ mJpegCallback = null;
+ mPreviewCallback = null;
+ mPostviewCallback = null;
+ mUsingPreviewAllocation = false;
+ mZoomListener = null;
+
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler = new EventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mEventHandler = new EventHandler(this, looper);
+ } else {
+ mEventHandler = null;
+ }
+
+ String packageName = ActivityThread.currentPackageName();
+
+ return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
+ }
+
Camera(int cameraId) {
int err = cameraInit(cameraId);
if (checkInitErrors(err)) {
@@ -369,7 +495,8 @@
String packageName = ActivityThread.currentPackageName();
- return native_setup(new WeakReference<Camera>(this), cameraId, packageName);
+ return native_setup(new WeakReference<Camera>(this), cameraId,
+ CAMERA_HAL_API_VERSION_UNSPECIFIED, packageName);
}
/**
@@ -396,7 +523,7 @@
release();
}
- private native final int native_setup(Object camera_this, int cameraId,
+ private native final int native_setup(Object camera_this, int cameraId, int halVersion,
String packageName);
private native final void native_release();
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 31896f5..2bc3dd4 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -74,4 +74,11 @@
int getLegacyParameters(int cameraId, out String[] parameters);
// Determines if a particular API version is supported; see ICameraService.h for version defines
int supportsCameraApi(int cameraId, int apiVersion);
+
+ int connectLegacy(ICameraClient client, int cameraId,
+ int halVersion,
+ String clientPackageName,
+ int clientUid,
+ // Container for an ICamera object
+ out BinderHolder device);
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 5a02435..dad1854 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -248,7 +248,6 @@
* <li>Manual frame duration control<ul>
* <li>{@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}</li>
* <li>{@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
- * <li>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</li>
* </ul>
* </li>
* <li>Manual exposure control<ul>
@@ -279,6 +278,9 @@
* result.</p>
* <p>A given camera device may also support additional manual sensor controls,
* but this capability only covers the above list of controls.</p>
+ * <p>If this is supported, {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} will
+ * additionally return a min frame duration that is greater than
+ * zero for each supported size-format combination.</p>
*
* @see CaptureRequest#BLACK_LEVEL_LOCK
* @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 54d9c3c..aa2f026 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -169,7 +169,6 @@
}
int numSurfaces = mSurfaces.size();
if (numSurfaces > 0) {
- surfaces = new ArrayList<Surface>();
for (int i = 0; i < numSurfaces; ++i) {
surfaces.add(mSurfaces.valueAt(i));
}
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
index 0382557..5d44fd2 100644
--- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
@@ -100,6 +100,7 @@
break;
case MSG_ALLOW_FRAMES:
mDroppingFrames = false;
+ break;
default:
Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
break;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index c34a34d..3f9c6ed 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -50,6 +50,7 @@
public static final String DEBUG_PROP = "HAL1ShimLogging";
private final String TAG;
+ private static final boolean DEBUG = false;
private final int mCameraId;
private final ICameraDeviceCallbacks mDeviceCallbacks;
private final CameraDeviceState mDeviceState = new CameraDeviceState();
@@ -84,6 +85,9 @@
mResultHandler.post(new Runnable() {
@Override
public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "doing onError callback.");
+ }
try {
mDeviceCallbacks.onCameraError(errorCode, extras);
} catch (RemoteException e) {
@@ -92,13 +96,14 @@
}
}
});
-
-
}
@Override
public void onConfiguring() {
// Do nothing
+ if (DEBUG) {
+ Log.d(TAG, "doing onConfiguring callback.");
+ }
}
@Override
@@ -108,6 +113,9 @@
mResultHandler.post(new Runnable() {
@Override
public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "doing onIdle callback.");
+ }
try {
mDeviceCallbacks.onCameraIdle();
} catch (RemoteException e) {
@@ -126,6 +134,9 @@
mResultHandler.post(new Runnable() {
@Override
public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "doing onCaptureStarted callback.");
+ }
try {
// TODO: Don't fake timestamp
mDeviceCallbacks.onCaptureStarted(extras, timestamp);
@@ -135,7 +146,6 @@
}
}
});
-
}
@Override
@@ -145,6 +155,9 @@
mResultHandler.post(new Runnable() {
@Override
public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "doing onCaptureResult callback.");
+ }
try {
// TODO: Don't fake metadata
mDeviceCallbacks.onResultReceived(result, extras);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 8bb066f..e675f87 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -44,6 +44,11 @@
private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
+ private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // ms
+ private static final long APPROXIMATE_SENSOR_AREA = (1 << 20); // 8mp
+ private static final long APPROXIMATE_JPEG_ENCODE_TIME = 600; // ms
+ private static final long NS_PER_MS = 1000000;
+
/**
* Create characteristics for a legacy device by mapping the {@code parameters}
* and {@code info}
@@ -91,9 +96,6 @@
}
private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
- // TODO: set non-empty durations
- m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[] {} );
- m.set(SCALER_AVAILABLE_STALL_DURATIONS, new StreamConfigurationDuration[] {} );
ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>();
/*
@@ -122,10 +124,25 @@
String.format("mapStreamConfigs - Skipping non-public format %x", format));
}
}
+
+ List<Camera.Size> jpegSizes = p.getSupportedPictureSizes();
appendStreamConfig(availableStreamConfigs,
HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes());
m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
availableStreamConfigs.toArray(new StreamConfiguration[0]));
+
+ // No frame durations available
+ m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[0]);
+
+ StreamConfigurationDuration[] jpegStalls =
+ new StreamConfigurationDuration[jpegSizes.size()];
+ int i = 0;
+ for (Camera.Size s : jpegSizes) {
+ jpegStalls[i++] = new StreamConfigurationDuration(HAL_PIXEL_FORMAT_BLOB, s.width,
+ s.height, calculateJpegStallDuration(s));
+ }
+ // Set stall durations for jpeg, other formats use default stall duration
+ m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
}
private static void appendStreamConfig(
@@ -136,4 +153,17 @@
configs.add(config);
}
}
+
+ /**
+ * Return the stall duration for a given output jpeg size in nanoseconds.
+ *
+ * <p>An 8mp image is chosen to have a stall duration of 0.8 seconds.</p>
+ */
+ private static long calculateJpegStallDuration(Camera.Size size) {
+ long baseDuration = APPROXIMATE_CAPTURE_DELAY_MS * NS_PER_MS; // 200ms for capture
+ long area = size.width * (long) size.height;
+ long stallPerArea = APPROXIMATE_JPEG_ENCODE_TIME * NS_PER_MS /
+ APPROXIMATE_SENSOR_AREA; // 600ms stall for 8mp
+ return baseDuration + area * stallPerArea;
+ }
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 7b522ff..bf250a1 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -71,6 +71,8 @@
private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
private boolean mPreviewRunning = false;
+ private volatile long mLastJpegTimestamp;
+ private volatile long mLastPreviewTimestamp;
private volatile RequestHolder mInFlightPreview;
private volatile RequestHolder mInFlightJpeg;
@@ -78,6 +80,7 @@
private List<Surface> mCallbackOutputs = new ArrayList<Surface>();
private GLThreadManager mGLThreadManager;
private SurfaceTexture mPreviewTexture;
+ private Camera.Parameters mParams;
private Size mIntermediateBufferSize;
@@ -86,6 +89,7 @@
private Surface mDummySurface;
private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
+ private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
/**
* Container object for Configure messages.
@@ -209,23 +213,34 @@
}
};
+ private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
+ @Override
+ public void onShutter() {
+ mLastJpegTimestamp = SystemClock.elapsedRealtimeNanos();
+ }
+ };
+
private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ RequestHolder holder = mInFlightPreview;
+ if (holder == null) {
+ mGLThreadManager.queueNewFrame(null);
+ Log.w(TAG, "Dropping preview frame.");
+ return;
+ }
+
if (DEBUG) {
mPrevCounter.countAndLog();
}
- RequestHolder holder = mInFlightPreview;
- if (holder == null) {
- Log.w(TAG, "Dropping preview frame.");
- mInFlightPreview = null;
- return;
- }
+ mInFlightPreview = null;
+
if (holder.hasPreviewTargets()) {
mGLThreadManager.queueNewFrame(holder.getHolderTargets());
}
+ mLastPreviewTimestamp = surfaceTexture.getTimestamp();
mReceivedPreview.open();
}
};
@@ -252,7 +267,7 @@
}
mInFlightJpeg = request;
// TODO: Hook up shutter callback to CameraDeviceStateListener#onCaptureStarted
- mCamera.takePicture(/*shutter*/null, /*raw*/null, mJpegCallback);
+ mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
mPreviewRunning = false;
}
@@ -312,7 +327,7 @@
break;
}
}
-
+ mParams = mCamera.getParameters();
if (mPreviewOutputs.size() > 0) {
List<Size> outputSizes = new ArrayList<>(outputs.size());
for (Surface s : mPreviewOutputs) {
@@ -323,13 +338,11 @@
Size largestOutput = findLargestByArea(outputSizes);
- Camera.Parameters params = mCamera.getParameters();
-
// Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
- List<Size> supportedJpegSizes = convertSizeList(params.getSupportedPictureSizes());
+ List<Size> supportedJpegSizes = convertSizeList(mParams.getSupportedPictureSizes());
Size largestJpegDimen = findLargestByArea(supportedJpegSizes);
- List<Size> supportedPreviewSizes = convertSizeList(params.getSupportedPreviewSizes());
+ List<Size> supportedPreviewSizes = convertSizeList(mParams.getSupportedPreviewSizes());
// Use smallest preview dimension with same aspect ratio as sensor that is >= than all
// of the configured output dimensions. If none exists, fall back to using the largest
@@ -428,6 +441,9 @@
return true;
}
+ if (DEBUG) {
+ Log.d(TAG, "Request thread handling message:" + msg.what);
+ }
switch (msg.what) {
case MSG_CONFIGURE_OUTPUTS:
ConfigureHolder config = (ConfigureHolder) msg.obj;
@@ -460,6 +476,7 @@
nextBurst.first.produceRequestHolders(nextBurst.second);
for (RequestHolder holder : requests) {
mDeviceState.setCaptureStart(holder);
+ long timestamp = 0;
try {
if (holder.hasPreviewTargets()) {
mReceivedPreview.close();
@@ -468,6 +485,7 @@
// TODO: report error to CameraDevice
Log.e(TAG, "Hit timeout for preview callback!");
}
+ timestamp = mLastPreviewTimestamp;
}
if (holder.hasJpegTargets()) {
mReceivedJpeg.close();
@@ -478,16 +496,19 @@
Log.e(TAG, "Hit timeout for jpeg callback!");
}
mInFlightJpeg = null;
+ timestamp = mLastJpegTimestamp;
}
} catch (IOException e) {
// TODO: err handling
throw new IOError(e);
}
- Camera.Parameters params = mCamera.getParameters();
- CameraMetadataNative result = convertResultMetadata(params,
- holder.getRequest());
+ CameraMetadataNative result = convertResultMetadata(mParams,
+ holder.getRequest(), timestamp);
mDeviceState.setCaptureResult(holder, result);
}
+ if (DEBUG) {
+ mRequestCounter.countAndLog();
+ }
break;
case MSG_CLEANUP:
mCleanup = true;
@@ -507,9 +528,11 @@
};
private CameraMetadataNative convertResultMetadata(Camera.Parameters params,
- CaptureRequest request) {
+ CaptureRequest request,
+ long timestamp) {
CameraMetadataNative result = new CameraMetadataNative();
result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
+ result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
// TODO: Remaining result metadata tags conversions.
return result;
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 49f419f..e9d32f0 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -486,6 +486,7 @@
}
checkGlError("before updateTexImage");
mSurfaceTexture.updateTexImage();
+ if (targetSurfaces == null) return;
for (EGLSurfaceHolder holder : mSurfaces) {
if (targetSurfaces.contains(holder.surface)) {
makeCurrent(holder.eglSurface);
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 3036425..fff171b 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -63,6 +63,12 @@
public final class StreamConfigurationMap {
private static final String TAG = "StreamConfigurationMap";
+
+ /**
+ * Indicates that a minimum frame duration is not available for a particular configuration.
+ */
+ public static final long NO_MIN_FRAME_DURATION = 0;
+
/**
* Create a new {@link StreamConfigurationMap}.
*
@@ -359,7 +365,9 @@
*
* @param format an image format from {@link ImageFormat} or {@link PixelFormat}
* @param size an output-compatible size
- * @return a minimum frame duration {@code >=} 0 in nanoseconds
+ * @return a minimum frame duration {@code >} 0 in nanoseconds, or
+ * {@link #NO_MIN_FRAME_DURATION} if the minimum frame duration is not available (this
+ * can only occur on limited mode devices).
*
* @throws IllegalArgumentException if {@code format} or {@code size} was not supported
* @throws NullPointerException if {@code size} was {@code null}
@@ -406,7 +414,9 @@
* a class which is supported by {@link #isOutputSupportedFor(Class)} and has a
* non-empty array returned by {@link #getOutputSizes(Class)}
* @param size an output-compatible size
- * @return a minimum frame duration {@code >=} 0 in nanoseconds
+ * @return a minimum frame duration {@code >} 0 in nanoseconds, or
+ * {@link #NO_MIN_FRAME_DURATION} if the minimum frame duration is not available (this
+ * can only occur on limited mode devices).
*
* @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
* @throws NullPointerException if {@code size} or {@code klass} was {@code null}
@@ -892,7 +902,7 @@
private long getDurationDefault(int duration) {
switch (duration) {
case DURATION_MIN_FRAME:
- throw new AssertionError("Minimum frame durations are required to be listed");
+ return NO_MIN_FRAME_DURATION;
case DURATION_STALL:
return 0L; // OK. A lack of a stall duration implies a 0 stall duration
default:
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 468cfe0..96db772 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -226,14 +226,14 @@
public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
/**
- * Key for user restrictions. Specifies if a user is disallowed from configuring
+ * Key for user restrictions. Specifies if a user is disallowed from controlling
* applications in Settings. The default value is <code>false</code>.
* <p>
* Type: Boolean
* @see #setUserRestrictions(Bundle)
* @see #getUserRestrictions()
*/
- public static final String DISALLOW_CONFIG_APPS = "no_config_apps";
+ public static final String DISALLOW_APPS_CONTROL = "no_control_apps";
/**
* Key for user restrictions. Specifies if a user is disallowed from mounting
@@ -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/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 1e7d7f1..8038a38 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1694,7 +1694,7 @@
*/
public static final class Entity implements BaseColumns, ContactsColumns,
ContactNameColumns, RawContactsColumns, BaseSyncColumns, SyncColumns, DataColumns,
- StatusColumns, ContactOptionsColumns, ContactStatusColumns {
+ StatusColumns, ContactOptionsColumns, ContactStatusColumns, DataUsageStatColumns {
/**
* no public constructor since this is a utility class
*/
@@ -7813,6 +7813,25 @@
"pinned_position_update");
/**
+ * <p>
+ * The method to invoke in order to undemote a formerly demoted contact. The contact id of
+ * the contact must be provided as an argument. If the contact was not previously demoted,
+ * nothing will be done.
+ * </p>
+ *
+ * <p>
+ * Example:
+ * <pre>
+ * final long contactId = 10;
+ * resolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
+ * String.valueOf(contactId), null);
+ * </pre>
+ *
+ * </p>
+ */
+ public static final String UNDEMOTE_METHOD = "undemote";
+
+ /**
* Default value for the pinned position of an unpinned contact. Also equal to
* {@link Integer#MAX_VALUE}.
*/
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/util/PathParser.java b/core/java/android/util/PathParser.java
new file mode 100644
index 0000000..f90ce51
--- /dev/null
+++ b/core/java/android/util/PathParser.java
@@ -0,0 +1,528 @@
+/*
+ * 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.util;
+
+import android.graphics.Path;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * @hide
+ */
+public class PathParser {
+ static final String LOGTAG = PathParser.class.getSimpleName();
+
+ /**
+ * @param pathData The string representing a path, the same as "d" string in svg file.
+ * @return the generated Path object.
+ */
+ public static Path createPathFromPathData(String pathData) {
+ Path path = new Path();
+ PathDataNode[] nodes = createNodesFromPathData(pathData);
+ if (nodes != null) {
+ PathDataNode.nodesToPath(nodes, path);
+ return path;
+ }
+ return null;
+ }
+
+ /**
+ * @param pathData The string representing a path, the same as "d" string in svg file.
+ * @return an array of the PathDataNode.
+ */
+ public static PathDataNode[] createNodesFromPathData(String pathData) {
+ int start = 0;
+ int end = 1;
+
+ ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
+ while (end < pathData.length()) {
+ end = nextStart(pathData, end);
+ String s = pathData.substring(start, end);
+ float[] val = getFloats(s);
+ addNode(list, s.charAt(0), val);
+
+ start = end;
+ end++;
+ }
+ if ((end - start) == 1 && start < pathData.length()) {
+ addNode(list, pathData.charAt(start), new float[0]);
+ }
+ return list.toArray(new PathDataNode[list.size()]);
+ }
+
+ private static int nextStart(String s, int end) {
+ char c;
+
+ while (end < s.length()) {
+ c = s.charAt(end);
+ if (((c - 'A') * (c - 'Z') <= 0) || (((c - 'a') * (c - 'z') <= 0))) {
+ return end;
+ }
+ end++;
+ }
+ return end;
+ }
+
+ private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) {
+ list.add(new PathDataNode(cmd, val));
+ }
+
+
+ /**
+ * Parse the floats in the string.
+ * This is an optimized version of parseFloat(s.split(",|\\s"));
+ *
+ * @param s the string containing a command and list of floats
+ * @return array of floats
+ */
+ private static float[] getFloats(String s) {
+ if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
+ return new float[0];
+ }
+ try {
+ float[] tmp = new float[s.length()];
+ int count = 0;
+ int pos = 1, end;
+ while ((end = extract(s, pos)) >= 0) {
+ if (pos < end) {
+ tmp[count++] = Float.parseFloat(s.substring(pos, end));
+ }
+ pos = end + 1;
+ }
+ // handle the final float if there is one
+ if (pos < s.length()) {
+ tmp[count++] = Float.parseFloat(s.substring(pos, s.length()));
+ }
+ return Arrays.copyOf(tmp, count);
+ } catch (NumberFormatException e){
+ Log.e(LOGTAG,"error in parsing \""+s+"\"");
+ throw e;
+ }
+ }
+
+ /**
+ * Calculate the position of the next comma or space
+ * @param s the string to search
+ * @param start the position to start searching
+ * @return the position of the next comma or space or -1 if none found
+ */
+ private static int extract(String s, int start) {
+ int space = s.indexOf(' ', start);
+ int comma = s.indexOf(',', start);
+ if (space == -1) {
+ return comma;
+ }
+ if (comma == -1) {
+ return space;
+ }
+ return (comma > space) ? space : comma;
+ }
+
+ public static class PathDataNode {
+ private char mType;
+ private float[] mParams;
+
+ private PathDataNode(char type, float[] params) {
+ mType = type;
+ mParams = params;
+ }
+
+ private PathDataNode(PathDataNode n) {
+ mType = n.mType;
+ mParams = Arrays.copyOf(n.mParams, n.mParams.length);
+ }
+
+ public static void nodesToPath(PathDataNode[] node, Path path) {
+ float[] current = new float[4];
+ char previousCommand = 'm';
+ for (int i = 0; i < node.length; i++) {
+ addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
+ previousCommand = node[i].mType;
+ }
+ }
+
+ private static void addCommand(Path path, float[] current,
+ char previousCmd, char cmd, float[] val) {
+
+ int incr = 2;
+ float currentX = current[0];
+ float currentY = current[1];
+ float ctrlPointX = current[2];
+ float ctrlPointY = current[3];
+ float reflectiveCtrlPointX;
+ float reflectiveCtrlPointY;
+
+ switch (cmd) {
+ case 'z':
+ case 'Z':
+ path.close();
+ return;
+ case 'm':
+ case 'M':
+ case 'l':
+ case 'L':
+ case 't':
+ case 'T':
+ incr = 2;
+ break;
+ case 'h':
+ case 'H':
+ case 'v':
+ case 'V':
+ incr = 1;
+ break;
+ case 'c':
+ case 'C':
+ incr = 6;
+ break;
+ case 's':
+ case 'S':
+ case 'q':
+ case 'Q':
+ incr = 4;
+ break;
+ case 'a':
+ case 'A':
+ incr = 7;
+ break;
+ }
+ for (int k = 0; k < val.length; k += incr) {
+ switch (cmd) {
+ case 'm': // moveto - Start a new sub-path (relative)
+ path.rMoveTo(val[k + 0], val[k + 1]);
+ currentX += val[k + 0];
+ currentY += val[k + 1];
+ break;
+ case 'M': // moveto - Start a new sub-path
+ path.moveTo(val[k + 0], val[k + 1]);
+ currentX = val[k + 0];
+ currentY = val[k + 1];
+ break;
+ case 'l': // lineto - Draw a line from the current point (relative)
+ path.rLineTo(val[k + 0], val[k + 1]);
+ currentX += val[k + 0];
+ currentY += val[k + 1];
+ break;
+ case 'L': // lineto - Draw a line from the current point
+ path.lineTo(val[k + 0], val[k + 1]);
+ currentX = val[k + 0];
+ currentY = val[k + 1];
+ break;
+ case 'z': // closepath - Close the current subpath
+ case 'Z': // closepath - Close the current subpath
+ path.close();
+ break;
+ case 'h': // horizontal lineto - Draws a horizontal line (relative)
+ path.rLineTo(val[k + 0], 0);
+ currentX += val[k + 0];
+ break;
+ case 'H': // horizontal lineto - Draws a horizontal line
+ path.lineTo(val[k + 0], currentY);
+ currentX = val[k + 0];
+ break;
+ case 'v': // vertical lineto - Draws a vertical line from the current point (r)
+ path.rLineTo(0, val[k + 0]);
+ currentY += val[k + 0];
+ break;
+ case 'V': // vertical lineto - Draws a vertical line from the current point
+ path.lineTo(currentX, val[k + 0]);
+ currentY = val[k + 0];
+ break;
+ case 'c': // curveto - Draws a cubic Bézier curve (relative)
+ path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+ val[k + 4], val[k + 5]);
+
+ ctrlPointX = currentX + val[k + 2];
+ ctrlPointY = currentY + val[k + 3];
+ currentX += val[k + 4];
+ currentY += val[k + 5];
+
+ break;
+ case 'C': // curveto - Draws a cubic Bézier curve
+ path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+ val[k + 4], val[k + 5]);
+ currentX = val[k + 4];
+ currentY = val[k + 5];
+ ctrlPointX = val[k + 2];
+ ctrlPointY = val[k + 3];
+ break;
+ case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
+ reflectiveCtrlPointX = 0;
+ reflectiveCtrlPointY = 0;
+ if (previousCmd == 'c' || previousCmd == 's'
+ || previousCmd == 'C' || previousCmd == 'S') {
+ reflectiveCtrlPointX = currentX - ctrlPointX;
+ reflectiveCtrlPointY = currentY - ctrlPointY;
+ }
+ path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ val[k + 0], val[k + 1],
+ val[k + 2], val[k + 3]);
+
+ ctrlPointX = currentX + val[k + 0];
+ ctrlPointY = currentY + val[k + 1];
+ currentX += val[k + 2];
+ currentY += val[k + 3];
+ break;
+ case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
+ reflectiveCtrlPointX = currentX;
+ reflectiveCtrlPointY = currentY;
+ if (previousCmd == 'c' || previousCmd == 's'
+ || previousCmd == 'C' || previousCmd == 'S') {
+ reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+ reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+ }
+ path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+ ctrlPointX = val[k + 0];
+ ctrlPointY = val[k + 1];
+ currentX = val[k + 2];
+ currentY = val[k + 3];
+ break;
+ case 'q': // Draws a quadratic Bézier (relative)
+ path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+ ctrlPointX = currentX + val[k + 0];
+ ctrlPointY = currentY + val[k + 1];
+ currentX += val[k + 2];
+ currentY += val[k + 3];
+ break;
+ case 'Q': // Draws a quadratic Bézier
+ path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+ ctrlPointX = val[k + 0];
+ ctrlPointY = val[k + 1];
+ currentX = val[k + 2];
+ currentY = val[k + 3];
+ break;
+ case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
+ reflectiveCtrlPointX = 0;
+ reflectiveCtrlPointY = 0;
+ if (previousCmd == 'q' || previousCmd == 't'
+ || previousCmd == 'Q' || previousCmd == 'T') {
+ reflectiveCtrlPointX = currentX - ctrlPointX;
+ reflectiveCtrlPointY = currentY - ctrlPointY;
+ }
+ path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ val[k + 0], val[k + 1]);
+ ctrlPointX = currentX + reflectiveCtrlPointX;
+ ctrlPointY = currentY + reflectiveCtrlPointY;
+ currentX += val[k + 0];
+ currentY += val[k + 1];
+ break;
+ case 'T': // Draws a quadratic Bézier curve (reflective control point)
+ reflectiveCtrlPointX = currentX;
+ reflectiveCtrlPointY = currentY;
+ if (previousCmd == 'q' || previousCmd == 't'
+ || previousCmd == 'Q' || previousCmd == 'T') {
+ reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+ reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+ }
+ path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ val[k + 0], val[k + 1]);
+ ctrlPointX = reflectiveCtrlPointX;
+ ctrlPointY = reflectiveCtrlPointY;
+ currentX = val[k + 0];
+ currentY = val[k + 1];
+ break;
+ case 'a': // Draws an elliptical arc
+ // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+ drawArc(path,
+ currentX,
+ currentY,
+ val[k + 5] + currentX,
+ val[k + 6] + currentY,
+ val[k + 0],
+ val[k + 1],
+ val[k + 2],
+ val[k + 3] != 0,
+ val[k + 4] != 0);
+ currentX += val[k + 5];
+ currentY += val[k + 6];
+ ctrlPointX = currentX;
+ ctrlPointY = currentY;
+ break;
+ case 'A': // Draws an elliptical arc
+ drawArc(path,
+ currentX,
+ currentY,
+ val[k + 5],
+ val[k + 6],
+ val[k + 0],
+ val[k + 1],
+ val[k + 2],
+ val[k + 3] != 0,
+ val[k + 4] != 0);
+ currentX = val[k + 5];
+ currentY = val[k + 6];
+ ctrlPointX = currentX;
+ ctrlPointY = currentY;
+ break;
+ }
+ previousCmd = cmd;
+ }
+ current[0] = currentX;
+ current[1] = currentY;
+ current[2] = ctrlPointX;
+ current[3] = ctrlPointY;
+ }
+
+ private static void drawArc(Path p,
+ float x0,
+ float y0,
+ float x1,
+ float y1,
+ float a,
+ float b,
+ float theta,
+ boolean isMoreThanHalf,
+ boolean isPositiveArc) {
+
+ /* Convert rotation angle from degrees to radians */
+ double thetaD = Math.toRadians(theta);
+ /* Pre-compute rotation matrix entries */
+ double cosTheta = Math.cos(thetaD);
+ double sinTheta = Math.sin(thetaD);
+ /* Transform (x0, y0) and (x1, y1) into unit space */
+ /* using (inverse) rotation, followed by (inverse) scale */
+ double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
+ double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
+ double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
+ double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
+
+ /* Compute differences and averages */
+ double dx = x0p - x1p;
+ double dy = y0p - y1p;
+ double xm = (x0p + x1p) / 2;
+ double ym = (y0p + y1p) / 2;
+ /* Solve for intersecting unit circles */
+ double dsq = dx * dx + dy * dy;
+ if (dsq == 0.0) {
+ Log.w(LOGTAG, " Points are coincident");
+ return; /* Points are coincident */
+ }
+ double disc = 1.0 / dsq - 1.0 / 4.0;
+ if (disc < 0.0) {
+ Log.w(LOGTAG, "Points are too far apart " + dsq);
+ float adjust = (float) (Math.sqrt(dsq) / 1.99999);
+ drawArc(p, x0, y0, x1, y1, a * adjust,
+ b * adjust, theta, isMoreThanHalf, isPositiveArc);
+ return; /* Points are too far apart */
+ }
+ double s = Math.sqrt(disc);
+ double sdx = s * dx;
+ double sdy = s * dy;
+ double cx;
+ double cy;
+ if (isMoreThanHalf == isPositiveArc) {
+ cx = xm - sdy;
+ cy = ym + sdx;
+ } else {
+ cx = xm + sdy;
+ cy = ym - sdx;
+ }
+
+ double eta0 = Math.atan2((y0p - cy), (x0p - cx));
+
+ double eta1 = Math.atan2((y1p - cy), (x1p - cx));
+
+ double sweep = (eta1 - eta0);
+ if (isPositiveArc != (sweep >= 0)) {
+ if (sweep > 0) {
+ sweep -= 2 * Math.PI;
+ } else {
+ sweep += 2 * Math.PI;
+ }
+ }
+
+ cx *= a;
+ cy *= b;
+ double tcx = cx;
+ cx = cx * cosTheta - cy * sinTheta;
+ cy = tcx * sinTheta + cy * cosTheta;
+
+ arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
+ }
+
+ /**
+ * Converts an arc to cubic Bezier segments and records them in p.
+ *
+ * @param p The target for the cubic Bezier segments
+ * @param cx The x coordinate center of the ellipse
+ * @param cy The y coordinate center of the ellipse
+ * @param a The radius of the ellipse in the horizontal direction
+ * @param b The radius of the ellipse in the vertical direction
+ * @param e1x E(eta1) x coordinate of the starting point of the arc
+ * @param e1y E(eta2) y coordinate of the starting point of the arc
+ * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
+ * @param start The start angle of the arc on the ellipse
+ * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+ */
+ private static void arcToBezier(Path p,
+ double cx,
+ double cy,
+ double a,
+ double b,
+ double e1x,
+ double e1y,
+ double theta,
+ double start,
+ double sweep) {
+ // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
+ // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+ // Maximum of 45 degrees per cubic Bezier segment
+ int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
+
+ double eta1 = start;
+ double cosTheta = Math.cos(theta);
+ double sinTheta = Math.sin(theta);
+ double cosEta1 = Math.cos(eta1);
+ double sinEta1 = Math.sin(eta1);
+ double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
+ double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
+
+ double anglePerSegment = sweep / numSegments;
+ for (int i = 0; i < numSegments; i++) {
+ double eta2 = eta1 + anglePerSegment;
+ double sinEta2 = Math.sin(eta2);
+ double cosEta2 = Math.cos(eta2);
+ double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
+ double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
+ double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
+ double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
+ double tanDiff2 = Math.tan((eta2 - eta1) / 2);
+ double alpha =
+ Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
+ double q1x = e1x + alpha * ep1x;
+ double q1y = e1y + alpha * ep1y;
+ double q2x = e2x - alpha * ep2x;
+ double q2y = e2y - alpha * ep2y;
+
+ p.cubicTo((float) q1x,
+ (float) q1y,
+ (float) q2x,
+ (float) q2y,
+ (float) e2x,
+ (float) e2y);
+ eta1 = eta2;
+ e1x = e2x;
+ e1y = e2y;
+ ep1x = ep2x;
+ ep1y = ep2y;
+ }
+ }
+
+ }
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d7a913d..76a6f52 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -599,6 +599,40 @@
}
/**
+ * Gets the app VSYNC offset, in nanoseconds. This is a positive value indicating
+ * the phase offset of the VSYNC events provided by Choreographer relative to the
+ * display refresh. For example, if Choreographer reports that the refresh occurred
+ * at time N, it actually occurred at (N - appVsyncOffset).
+ * <p>
+ * Apps generally do not need to be aware of this. It's only useful for fine-grained
+ * A/V synchronization.
+ */
+ public long getAppVsyncOffsetNanos() {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.appVsyncOffsetNanos;
+ }
+ }
+
+ /**
+ * This is how far in advance a buffer must be queued for presentation at
+ * a given time. If you want a buffer to appear on the screen at
+ * time N, you must submit the buffer before (N - presentationDeadline).
+ * <p>
+ * The desired presentation time for GLES rendering may be set with
+ * {@link android.opengl.EGLExt#eglPresentationTimeANDROID}. For video decoding, use
+ * {@link android.media.MediaCodec#releaseOutputBuffer(int, long)}. Times are
+ * expressed in nanoseconds, using the system monotonic clock
+ * ({@link System#nanoTime}).
+ */
+ public long getPresentationDeadlineNanos() {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.presentationDeadlineNanos;
+ }
+ }
+
+ /**
* Gets display metrics that describe the size and density of this display.
* <p>
* The size is adjusted based on the current rotation of the display.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b0fe0fa..98696c7 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -180,6 +180,20 @@
public float physicalYDpi;
/**
+ * This is a positive value indicating the phase offset of the VSYNC events provided by
+ * Choreographer relative to the display refresh. For example, if Choreographer reports
+ * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos).
+ */
+ public long appVsyncOffsetNanos;
+
+ /**
+ * This is how far in advance a buffer must be queued for presentation at
+ * a given time. If you want a buffer to appear on the screen at
+ * time N, you must submit the buffer before (N - bufferDeadlineNanos).
+ */
+ public long presentationDeadlineNanos;
+
+ /**
* The state of the display, such as {@link android.view.Display#STATE_ON}.
*/
public int state;
@@ -253,6 +267,8 @@
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
&& physicalYDpi == other.physicalYDpi
+ && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+ && presentationDeadlineNanos == other.presentationDeadlineNanos
&& state == other.state
&& ownerUid == other.ownerUid
&& Objects.equal(ownerPackageName, other.ownerPackageName);
@@ -286,6 +302,8 @@
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
+ appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+ presentationDeadlineNanos = other.presentationDeadlineNanos;
state = other.state;
ownerUid = other.ownerUid;
ownerPackageName = other.ownerPackageName;
@@ -314,6 +332,8 @@
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
+ appVsyncOffsetNanos = source.readLong();
+ presentationDeadlineNanos = source.readLong();
state = source.readInt();
ownerUid = source.readInt();
ownerPackageName = source.readString();
@@ -343,6 +363,8 @@
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
+ dest.writeLong(appVsyncOffsetNanos);
+ dest.writeLong(presentationDeadlineNanos);
dest.writeInt(state);
dest.writeInt(ownerUid);
dest.writeString(ownerPackageName);
@@ -450,6 +472,10 @@
sb.append(physicalYDpi);
sb.append(") dpi, layerStack ");
sb.append(layerStack);
+ sb.append(", appVsyncOff ");
+ sb.append(appVsyncOffsetNanos);
+ sb.append(", presDeadline ");
+ sb.append(presentationDeadlineNanos);
sb.append(", type ");
sb.append(Display.typeToString(type));
if (address != null) {
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 446bbc9..9fafc48 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) {
@@ -865,31 +853,7 @@
private static native void nDrawPoints(long renderer, float[] points,
int offset, int count, long paint);
- @SuppressWarnings("deprecation")
- @Override
- public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
- if (index < 0 || index + count > text.length || count * 2 > pos.length) {
- throw new IndexOutOfBoundsException();
- }
-
- nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
- }
-
- private static native void nDrawPosText(long renderer, char[] text, int index, int count,
- float[] pos, long paint);
-
- @SuppressWarnings("deprecation")
- @Override
- public void drawPosText(String text, float[] pos, Paint paint) {
- if (text.length() * 2 > pos.length) {
- throw new ArrayIndexOutOfBoundsException();
- }
-
- nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
- }
-
- private static native void nDrawPosText(long renderer, String text, int start, int end,
- float[] pos, long paint);
+ // Note: drawPosText just uses implementation in Canvas
@Override
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
@@ -981,22 +945,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/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 79f19b5..94d8f70 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -458,6 +458,8 @@
public float xDpi;
public float yDpi;
public boolean secure;
+ public long appVsyncOffsetNanos;
+ public long presentationDeadlineNanos;
public PhysicalDisplayInfo() {
}
@@ -479,7 +481,9 @@
&& density == other.density
&& xDpi == other.xDpi
&& yDpi == other.yDpi
- && secure == other.secure;
+ && secure == other.secure
+ && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+ && presentationDeadlineNanos == other.presentationDeadlineNanos;
}
@Override
@@ -495,6 +499,8 @@
xDpi = other.xDpi;
yDpi = other.yDpi;
secure = other.secure;
+ appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+ presentationDeadlineNanos = other.presentationDeadlineNanos;
}
// For debugging purposes
@@ -502,7 +508,8 @@
public String toString() {
return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
+ "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure
- + "}";
+ + ", appVsyncOffset " + appVsyncOffsetNanos
+ + ", bufferDeadline " + presentationDeadlineNanos + "}";
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9156216..d25ef16 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)) {
@@ -14254,6 +14240,7 @@
* @return True if the view is attached to a window and the window is
* hardware accelerated; false in any other case.
*/
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isHardwareAccelerated() {
return mAttachInfo != null && mAttachInfo.mHardwareAccelerated;
}
@@ -15531,6 +15518,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/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a02e76b..1e72625 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4386,7 +4386,7 @@
// Make sure we do not set both flags at the same time
int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
- if (child.mLayerType == LAYER_TYPE_SOFTWARE) {
+ if (child.mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
@@ -4497,7 +4497,7 @@
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
- if (mLayerType == LAYER_TYPE_SOFTWARE) {
+ if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
@@ -4515,7 +4515,7 @@
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
- if (mLayerType == LAYER_TYPE_SOFTWARE) {
+ if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
diff --git a/core/java/android/view/animation/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java
index da12ffb..945ecf0 100644
--- a/core/java/android/view/animation/PathInterpolator.java
+++ b/core/java/android/view/animation/PathInterpolator.java
@@ -21,6 +21,7 @@
import android.content.res.TypedArray;
import android.graphics.Path;
import android.util.AttributeSet;
+import android.util.PathParser;
import android.view.InflateException;
import com.android.internal.R;
@@ -102,28 +103,40 @@
}
private void parseInterpolatorFromTypeArray(TypedArray a) {
- if (!a.hasValue(R.styleable.PathInterpolator_controlX1)) {
- throw new InflateException("pathInterpolator requires the controlX1 attribute");
- } else if (!a.hasValue(R.styleable.PathInterpolator_controlY1)) {
- throw new InflateException("pathInterpolator requires the controlY1 attribute");
- }
- float x1 = a.getFloat(R.styleable.PathInterpolator_controlX1, 0);
- float y1 = a.getFloat(R.styleable.PathInterpolator_controlY1, 0);
-
- boolean hasX2 = a.hasValue(R.styleable.PathInterpolator_controlX2);
- boolean hasY2 = a.hasValue(R.styleable.PathInterpolator_controlY2);
-
- if (hasX2 != hasY2) {
- throw new InflateException(
- "pathInterpolator requires both controlX2 and controlY2 for cubic Beziers.");
- }
-
- if (!hasX2) {
- initQuad(x1, y1);
+ // If there is pathData defined in the xml file, then the controls points
+ // will be all coming from pathData.
+ if (a.hasValue(R.styleable.PathInterpolator_pathData)) {
+ String pathData = a.getString(R.styleable.PathInterpolator_pathData);
+ Path path = PathParser.createPathFromPathData(pathData);
+ if (path == null) {
+ throw new InflateException("The path is null, which is created"
+ + " from " + pathData);
+ }
+ initPath(path);
} else {
- float x2 = a.getFloat(R.styleable.PathInterpolator_controlX2, 0);
- float y2 = a.getFloat(R.styleable.PathInterpolator_controlY2, 0);
- initCubic(x1, y1, x2, y2);
+ if (!a.hasValue(R.styleable.PathInterpolator_controlX1)) {
+ throw new InflateException("pathInterpolator requires the controlX1 attribute");
+ } else if (!a.hasValue(R.styleable.PathInterpolator_controlY1)) {
+ throw new InflateException("pathInterpolator requires the controlY1 attribute");
+ }
+ float x1 = a.getFloat(R.styleable.PathInterpolator_controlX1, 0);
+ float y1 = a.getFloat(R.styleable.PathInterpolator_controlY1, 0);
+
+ boolean hasX2 = a.hasValue(R.styleable.PathInterpolator_controlX2);
+ boolean hasY2 = a.hasValue(R.styleable.PathInterpolator_controlY2);
+
+ if (hasX2 != hasY2) {
+ throw new InflateException(
+ "pathInterpolator requires both controlX2 and controlY2 for cubic Beziers.");
+ }
+
+ if (!hasX2) {
+ initQuad(x1, y1);
+ } else {
+ float x2 = a.getFloat(R.styleable.PathInterpolator_controlX2, 0);
+ float y2 = a.getFloat(R.styleable.PathInterpolator_controlY2, 0);
+ initCubic(x1, y1, x2, y2);
+ }
}
}
@@ -216,5 +229,4 @@
float endY = mY[endIndex];
return startY + (fraction * (endY - startY));
}
-
}
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/GridView.java b/core/java/android/widget/GridView.java
index 04b18c1..93810b3 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1326,12 +1326,28 @@
if (sel != null) {
positionSelector(INVALID_POSITION, sel);
mSelectedTop = sel.getTop();
- } else if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
- View child = getChildAt(mMotionPosition - mFirstPosition);
- if (child != null) positionSelector(mMotionPosition, child);
} else {
- mSelectedTop = 0;
- mSelectorRect.setEmpty();
+ final boolean inTouchMode = mTouchMode > TOUCH_MODE_DOWN
+ && mTouchMode < TOUCH_MODE_SCROLL;
+ if (inTouchMode) {
+ // If the user's finger is down, select the motion position.
+ final View child = getChildAt(mMotionPosition - mFirstPosition);
+ if (child != null) {
+ positionSelector(mMotionPosition, child);
+ }
+ } else if (mSelectedPosition != INVALID_POSITION) {
+ // If we had previously positioned the selector somewhere,
+ // put it back there. It might not match up with the data,
+ // but it's transitioning out so it's not a big deal.
+ final View child = getChildAt(mSelectorPosition - mFirstPosition);
+ if (child != null) {
+ positionSelector(mSelectorPosition, child);
+ }
+ } else {
+ // Otherwise, clear selection.
+ mSelectedTop = 0;
+ mSelectorRect.setEmpty();
+ }
}
// Attempt to restore accessibility focus, if necessary.
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/ListView.java b/core/java/android/widget/ListView.java
index eeb8015..1baeca8 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1718,14 +1718,24 @@
}
mSelectedTop = sel.getTop();
} else {
- // If the user's finger is down, select the motion position.
- // Otherwise, clear selection.
- if (mTouchMode == TOUCH_MODE_TAP || mTouchMode == TOUCH_MODE_DONE_WAITING) {
+ final boolean inTouchMode = mTouchMode == TOUCH_MODE_TAP
+ || mTouchMode == TOUCH_MODE_DONE_WAITING;
+ if (inTouchMode) {
+ // If the user's finger is down, select the motion position.
final View child = getChildAt(mMotionPosition - mFirstPosition);
- if (child != null) {
+ if (child != null) {
positionSelector(mMotionPosition, child);
}
+ } else if (mSelectorPosition != INVALID_POSITION) {
+ // If we had previously positioned the selector somewhere,
+ // put it back there. It might not match up with the data,
+ // but it's transitioning out so it's not a big deal.
+ final View child = getChildAt(mSelectorPosition - mFirstPosition);
+ if (child != null) {
+ positionSelector(mSelectorPosition, child);
+ }
} else {
+ // Otherwise, clear selection.
mSelectedTop = 0;
mSelectorRect.setEmpty();
}
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..5b8e854 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -90,6 +90,7 @@
private boolean mSplitTrack;
private CharSequence mTextOn;
private CharSequence mTextOff;
+ private boolean mShowText;
private int mTouchMode;
private int mTouchSlop;
@@ -188,6 +189,7 @@
mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
mTextOff = a.getText(com.android.internal.R.styleable.Switch_textOff);
+ mShowText = a.getBoolean(com.android.internal.R.styleable.Switch_showText, true);
mThumbTextPadding = a.getDimensionPixelSize(
com.android.internal.R.styleable.Switch_thumbTextPadding, 0);
mSwitchMinWidth = a.getDimensionPixelSize(
@@ -533,14 +535,37 @@
requestLayout();
}
+ /**
+ * Sets whether the on/off text should be displayed.
+ *
+ * @param showText {@code true} to display on/off text
+ * @hide
+ */
+ public void setShowText(boolean showText) {
+ if (mShowText != showText) {
+ mShowText = showText;
+ requestLayout();
+ }
+ }
+
+ /**
+ * @return whether the on/off text should be displayed
+ * @hide
+ */
+ public boolean getShowText() {
+ return mShowText;
+ }
+
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mOnLayout == null) {
- mOnLayout = makeLayout(mTextOn);
- }
+ if (mShowText) {
+ if (mOnLayout == null) {
+ mOnLayout = makeLayout(mTextOn);
+ }
- if (mOffLayout == null) {
- mOffLayout = makeLayout(mTextOff);
+ if (mOffLayout == null) {
+ mOffLayout = makeLayout(mTextOff);
+ }
}
mTrackDrawable.getPadding(mTempRect);
@@ -568,9 +593,10 @@
@Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
super.onPopulateAccessibilityEvent(event);
- Layout layout = isChecked() ? mOnLayout : mOffLayout;
- if (layout != null && !TextUtils.isEmpty(layout.getText())) {
- event.getText().add(layout.getText());
+
+ final CharSequence text = isChecked() ? mTextOn : mTextOff;
+ if (text != null) {
+ event.getText().add(text);
}
}
@@ -962,10 +988,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/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 7e11850..4995ea1d1 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -28,7 +28,6 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
-import android.webkit.WebViewFactory;
import com.android.internal.util.GrowingArrayUtils;
@@ -55,6 +54,11 @@
// that is done.
public static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours
+ // Minimum uptime period before committing. If the COMMIT_PERIOD has elapsed but
+ // the total uptime has not exceeded this amount, then the commit will be held until
+ // it is reached.
+ public static long COMMIT_UPTIME_PERIOD = 60*60*1000; // Must have at least 1 hour elapsed
+
public static final int STATE_NOTHING = -1;
public static final int STATE_PERSISTENT = 0;
public static final int STATE_TOP = 1;
@@ -81,6 +85,24 @@
public static final int PSS_USS_MAXIMUM = 6;
public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
+ public static final int SYS_MEM_USAGE_SAMPLE_COUNT = 0;
+ public static final int SYS_MEM_USAGE_CACHED_MINIMUM = 1;
+ public static final int SYS_MEM_USAGE_CACHED_AVERAGE = 2;
+ public static final int SYS_MEM_USAGE_CACHED_MAXIMUM = 3;
+ public static final int SYS_MEM_USAGE_FREE_MINIMUM = 4;
+ public static final int SYS_MEM_USAGE_FREE_AVERAGE = 5;
+ public static final int SYS_MEM_USAGE_FREE_MAXIMUM = 6;
+ public static final int SYS_MEM_USAGE_ZRAM_MINIMUM = 7;
+ public static final int SYS_MEM_USAGE_ZRAM_AVERAGE = 8;
+ public static final int SYS_MEM_USAGE_ZRAM_MAXIMUM = 9;
+ public static final int SYS_MEM_USAGE_KERNEL_MINIMUM = 10;
+ public static final int SYS_MEM_USAGE_KERNEL_AVERAGE = 11;
+ public static final int SYS_MEM_USAGE_KERNEL_MAXIMUM = 12;
+ public static final int SYS_MEM_USAGE_NATIVE_MINIMUM = 13;
+ public static final int SYS_MEM_USAGE_NATIVE_AVERAGE = 14;
+ public static final int SYS_MEM_USAGE_NATIVE_MAXIMUM = 15;
+ public static final int SYS_MEM_USAGE_COUNT = SYS_MEM_USAGE_NATIVE_MAXIMUM+1;
+
public static final int ADJ_NOTHING = -1;
public static final int ADJ_MEM_FACTOR_NORMAL = 0;
public static final int ADJ_MEM_FACTOR_MODERATE = 1;
@@ -174,7 +196,7 @@
static final String CSV_SEP = "\t";
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 14;
+ private static final int PARCEL_VERSION = 18;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535453;
@@ -200,11 +222,16 @@
public int mMemFactor = STATE_NOTHING;
public long mStartTime;
+ public int[] mSysMemUsageTable = null;
+ public int mSysMemUsageTableSize = 0;
+ public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT];
+
public long mTimePeriodStartClock;
public long mTimePeriodStartRealtime;
public long mTimePeriodEndRealtime;
+ public long mTimePeriodStartUptime;
+ public long mTimePeriodEndUptime;
String mRuntime;
- String mWebView;
boolean mRunning;
static final int LONGS_SIZE = 4096;
@@ -304,11 +331,77 @@
mMemFactorDurations[i] += other.mMemFactorDurations[i];
}
+ for (int i=0; i<other.mSysMemUsageTableSize; i++) {
+ int ent = other.mSysMemUsageTable[i];
+ int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ long[] longs = other.mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ addSysMemUsage(state, longs, ((ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK));
+ }
+
if (other.mTimePeriodStartClock < mTimePeriodStartClock) {
mTimePeriodStartClock = other.mTimePeriodStartClock;
mTimePeriodStartClockStr = other.mTimePeriodStartClockStr;
}
mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime;
+ mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime;
+ }
+
+ public void addSysMemUsage(long cachedMem, long freeMem, long zramMem, long kernelMem,
+ long nativeMem) {
+ if (mMemFactor != STATE_NOTHING) {
+ int state = mMemFactor * STATE_COUNT;
+ mSysMemUsageArgs[SYS_MEM_USAGE_SAMPLE_COUNT] = 1;
+ for (int i=0; i<3; i++) {
+ mSysMemUsageArgs[SYS_MEM_USAGE_CACHED_MINIMUM + i] = cachedMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_FREE_MINIMUM + i] = freeMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_ZRAM_MINIMUM + i] = zramMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_KERNEL_MINIMUM + i] = kernelMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_NATIVE_MINIMUM + i] = nativeMem;
+ }
+ addSysMemUsage(state, mSysMemUsageArgs, 0);
+ }
+ }
+
+ void addSysMemUsage(int state, long[] data, int dataOff) {
+ int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state);
+ int off;
+ if (idx >= 0) {
+ off = mSysMemUsageTable[idx];
+ } else {
+ mAddLongTable = mSysMemUsageTable;
+ mAddLongTableSize = mSysMemUsageTableSize;
+ off = addLongData(~idx, state, SYS_MEM_USAGE_COUNT);
+ mSysMemUsageTable = mAddLongTable;
+ mSysMemUsageTableSize = mAddLongTableSize;
+ }
+ long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
+ addSysMemUsage(longs, idx, data, dataOff);
+ }
+
+ static void addSysMemUsage(long[] dstData, int dstOff, long[] addData, int addOff) {
+ final long dstCount = dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT];
+ final long addCount = addData[addOff+SYS_MEM_USAGE_SAMPLE_COUNT];
+ if (dstCount == 0) {
+ dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = addCount;
+ for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i++) {
+ dstData[dstOff+i] = addData[addOff+i];
+ }
+ } else if (addCount > 0) {
+ dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = dstCount + addCount;
+ for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i+=3) {
+ if (dstData[dstOff+i] > addData[addOff+i]) {
+ dstData[dstOff+i] = addData[addOff+i];
+ }
+ dstData[dstOff+i+1] = (long)(
+ ((dstData[dstOff+i+1]*(double)dstCount)
+ + (addData[addOff+i+1]*(double)addCount))
+ / (dstCount+addCount) );
+ if (dstData[dstOff+i+2] < addData[addOff+i+2]) {
+ dstData[dstOff+i+2] = addData[addOff+i+2];
+ }
+ }
+ }
}
public static final Parcelable.Creator<ProcessStats> CREATOR
@@ -564,6 +657,164 @@
return totalTime;
}
+ static class PssAggr {
+ long pss = 0;
+ long samples = 0;
+
+ void add(long newPss, long newSamples) {
+ pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) )
+ / (samples+newSamples);
+ samples += newSamples;
+ }
+ }
+
+ public void computeTotalMemoryUse(TotalMemoryUseCollection data, long now) {
+ data.totalTime = 0;
+ for (int i=0; i<STATE_COUNT; i++) {
+ data.processStateWeight[i] = 0;
+ data.processStatePss[i] = 0;
+ data.processStateTime[i] = 0;
+ data.processStateSamples[i] = 0;
+ }
+ for (int i=0; i<SYS_MEM_USAGE_COUNT; i++) {
+ data.sysMemUsage[i] = 0;
+ }
+ data.sysMemCachedWeight = 0;
+ data.sysMemFreeWeight = 0;
+ data.sysMemZRamWeight = 0;
+ data.sysMemKernelWeight = 0;
+ data.sysMemNativeWeight = 0;
+ data.sysMemSamples = 0;
+ long[] totalMemUsage = new long[SYS_MEM_USAGE_COUNT];
+ for (int i=0; i<mSysMemUsageTableSize; i++) {
+ int ent = mSysMemUsageTable[i];
+ long[] longs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ int idx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK;
+ addSysMemUsage(totalMemUsage, 0, longs, idx);
+ }
+ for (int is=0; is<data.screenStates.length; is++) {
+ for (int im=0; im<data.memStates.length; im++) {
+ int memBucket = data.screenStates[is] + data.memStates[im];
+ int stateBucket = memBucket * STATE_COUNT;
+ long memTime = mMemFactorDurations[memBucket];
+ if (mMemFactor == memBucket) {
+ memTime += now - mStartTime;
+ }
+ data.totalTime += memTime;
+ int sysIdx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, stateBucket);
+ long[] longs = totalMemUsage;
+ int idx = 0;
+ if (sysIdx >= 0) {
+ int ent = mSysMemUsageTable[sysIdx];
+ long[] tmpLongs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ int tmpIdx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK;
+ if (tmpLongs[tmpIdx+SYS_MEM_USAGE_SAMPLE_COUNT] >= 3) {
+ addSysMemUsage(data.sysMemUsage, 0, longs, idx);
+ longs = tmpLongs;
+ idx = tmpIdx;
+ }
+ }
+ data.sysMemCachedWeight += longs[idx+SYS_MEM_USAGE_CACHED_AVERAGE]
+ * (double)memTime;
+ data.sysMemFreeWeight += longs[idx+SYS_MEM_USAGE_FREE_AVERAGE]
+ * (double)memTime;
+ data.sysMemZRamWeight += longs[idx+SYS_MEM_USAGE_ZRAM_AVERAGE]
+ * (double)memTime;
+ data.sysMemKernelWeight += longs[idx+SYS_MEM_USAGE_KERNEL_AVERAGE]
+ * (double)memTime;
+ data.sysMemNativeWeight += longs[idx+SYS_MEM_USAGE_NATIVE_AVERAGE]
+ * (double)memTime;
+ data.sysMemSamples += longs[idx+SYS_MEM_USAGE_SAMPLE_COUNT];
+ }
+ }
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int iproc=0; iproc<procMap.size(); iproc++) {
+ SparseArray<ProcessState> uids = procMap.valueAt(iproc);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final ProcessState proc = uids.valueAt(iu);
+ final PssAggr fgPss = new PssAggr();
+ final PssAggr bgPss = new PssAggr();
+ final PssAggr cachedPss = new PssAggr();
+ boolean havePss = false;
+ for (int i=0; i<proc.mDurationsTableSize; i++) {
+ int off = proc.mDurationsTable[i];
+ int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ int procState = type % STATE_COUNT;
+ long samples = proc.getPssSampleCount(type);
+ if (samples > 0) {
+ long avg = proc.getPssAverage(type);
+ havePss = true;
+ if (procState <= STATE_IMPORTANT_FOREGROUND) {
+ fgPss.add(avg, samples);
+ } else if (procState <= STATE_RECEIVER) {
+ bgPss.add(avg, samples);
+ } else {
+ cachedPss.add(avg, samples);
+ }
+ }
+ }
+ if (!havePss) {
+ continue;
+ }
+ boolean fgHasBg = false;
+ boolean fgHasCached = false;
+ boolean bgHasCached = false;
+ if (fgPss.samples < 3 && bgPss.samples > 0) {
+ fgHasBg = true;
+ fgPss.add(bgPss.pss, bgPss.samples);
+ }
+ if (fgPss.samples < 3 && cachedPss.samples > 0) {
+ fgHasCached = true;
+ fgPss.add(cachedPss.pss, cachedPss.samples);
+ }
+ if (bgPss.samples < 3 && cachedPss.samples > 0) {
+ bgHasCached = true;
+ bgPss.add(cachedPss.pss, cachedPss.samples);
+ }
+ if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) {
+ bgPss.add(fgPss.pss, fgPss.samples);
+ }
+ if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) {
+ cachedPss.add(bgPss.pss, bgPss.samples);
+ }
+ if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) {
+ cachedPss.add(fgPss.pss, fgPss.samples);
+ }
+ for (int i=0; i<proc.mDurationsTableSize; i++) {
+ final int off = proc.mDurationsTable[i];
+ final int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ long time = getLong(off, 0);
+ if (proc.mCurState == type) {
+ time += now - proc.mStartTime;
+ }
+ final int procState = type % STATE_COUNT;
+ data.processStateTime[procState] += time;
+ long samples = proc.getPssSampleCount(type);
+ long avg;
+ if (samples > 0) {
+ avg = proc.getPssAverage(type);
+ } else if (procState <= STATE_IMPORTANT_FOREGROUND) {
+ samples = fgPss.samples;
+ avg = fgPss.pss;
+ } else if (procState <= STATE_RECEIVER) {
+ samples = bgPss.samples;
+ avg = bgPss.pss;
+ } else {
+ samples = cachedPss.samples;
+ avg = cachedPss.pss;
+ }
+ double newAvg = ( (data.processStatePss[procState]
+ * (double)data.processStateSamples[procState])
+ + (avg*(double)samples)
+ ) / (data.processStateSamples[procState]+samples);
+ data.processStatePss[procState] = (long)newAvg;
+ data.processStateSamples[procState] += samples;
+ data.processStateWeight[procState] += avg * (double)time;
+ }
+ }
+ }
+ }
+
static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
int[] screenStates, int[] memStates, int[] procStates, long now) {
long totalTime = 0;
@@ -679,6 +930,62 @@
}
}
+ long getSysMemUsageValue(int state, int index) {
+ int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state);
+ return idx >= 0 ? getLong(mSysMemUsageTable[idx], index) : 0;
+ }
+
+ void dumpSysMemUsageCategory(PrintWriter pw, String prefix, String label,
+ int bucket, int index) {
+ pw.print(prefix); pw.print(label);
+ pw.print(": ");
+ printSizeValue(pw, getSysMemUsageValue(bucket, index) * 1024);
+ pw.print(" min, ");
+ printSizeValue(pw, getSysMemUsageValue(bucket, index + 1) * 1024);
+ pw.print(" avg, ");
+ printSizeValue(pw, getSysMemUsageValue(bucket, index+2) * 1024);
+ pw.println(" max");
+ }
+
+ void dumpSysMemUsage(PrintWriter pw, String prefix, int[] screenStates,
+ int[] memStates) {
+ int printedScreen = -1;
+ for (int is=0; is<screenStates.length; is++) {
+ int printedMem = -1;
+ for (int im=0; im<memStates.length; im++) {
+ final int iscreen = screenStates[is];
+ final int imem = memStates[im];
+ final int bucket = ((iscreen + imem) * STATE_COUNT);
+ long count = getSysMemUsageValue(bucket, SYS_MEM_USAGE_SAMPLE_COUNT);
+ if (count > 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ }
+ if (memStates.length > 1) {
+ printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '\0');
+ printedMem = imem;
+ }
+ pw.print(": ");
+ pw.print(count);
+ pw.println(" samples:");
+ dumpSysMemUsageCategory(pw, prefix, " Cached", bucket,
+ SYS_MEM_USAGE_CACHED_MINIMUM);
+ dumpSysMemUsageCategory(pw, prefix, " Free", bucket,
+ SYS_MEM_USAGE_FREE_MINIMUM);
+ dumpSysMemUsageCategory(pw, prefix, " ZRam", bucket,
+ SYS_MEM_USAGE_ZRAM_MINIMUM);
+ dumpSysMemUsageCategory(pw, prefix, " Kernel", bucket,
+ SYS_MEM_USAGE_KERNEL_MINIMUM);
+ dumpSysMemUsageCategory(pw, prefix, " Native", bucket,
+ SYS_MEM_USAGE_NATIVE_MINIMUM);
+ }
+ }
+ }
+ }
+
static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
int[] memStates, int[] procStates) {
final int NS = screenStates != null ? screenStates.length : 1;
@@ -1088,10 +1395,13 @@
mTimePeriodStartClock = System.currentTimeMillis();
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis();
mLongs.clear();
mLongs.add(new long[LONGS_SIZE]);
mNextLong = 0;
Arrays.fill(mMemFactorDurations, 0);
+ mSysMemUsageTable = null;
+ mSysMemUsageTableSize = 0;
mStartTime = 0;
mReadError = null;
mFlags = 0;
@@ -1220,12 +1530,17 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- long now = SystemClock.uptimeMillis();
+ writeToParcel(out, SystemClock.uptimeMillis(), flags);
+ }
+
+ /** @hide */
+ public void writeToParcel(Parcel out, long now, int flags) {
out.writeInt(MAGIC);
out.writeInt(PARCEL_VERSION);
out.writeInt(STATE_COUNT);
out.writeInt(ADJ_COUNT);
out.writeInt(PSS_COUNT);
+ out.writeInt(SYS_MEM_USAGE_COUNT);
out.writeInt(LONGS_SIZE);
mCommonStringToIndex = new ArrayMap<String, Integer>(mProcesses.mMap.size());
@@ -1268,8 +1583,9 @@
out.writeLong(mTimePeriodStartClock);
out.writeLong(mTimePeriodStartRealtime);
out.writeLong(mTimePeriodEndRealtime);
+ out.writeLong(mTimePeriodStartUptime);
+ out.writeLong(mTimePeriodEndUptime);
out.writeString(mRuntime);
- out.writeString(mWebView);
out.writeInt(mFlags);
out.writeInt(mLongs.size());
@@ -1287,6 +1603,13 @@
}
writeCompactedLongArray(out, mMemFactorDurations, mMemFactorDurations.length);
+ out.writeInt(mSysMemUsageTableSize);
+ for (int i=0; i<mSysMemUsageTableSize; i++) {
+ if (DEBUG_PARCEL) Slog.i(TAG, "Writing sys mem usage #" + i + ": "
+ + printLongOffset(mSysMemUsageTable[i]));
+ out.writeInt(mSysMemUsageTable[i]);
+ }
+
out.writeInt(NPROC);
for (int ip=0; ip<NPROC; ip++) {
writeCommonString(out, procMap.keyAt(ip));
@@ -1417,6 +1740,9 @@
if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
return;
}
+ if (!readCheckedInt(in, SYS_MEM_USAGE_COUNT, "sys mem usage count")) {
+ return;
+ }
if (!readCheckedInt(in, LONGS_SIZE, "longs size")) {
return;
}
@@ -1427,8 +1753,9 @@
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = in.readLong();
mTimePeriodEndRealtime = in.readLong();
+ mTimePeriodStartUptime = in.readLong();
+ mTimePeriodEndUptime = in.readLong();
mRuntime = in.readString();
- mWebView = in.readString();
mFlags = in.readInt();
final int NLONGS = in.readInt();
@@ -1447,6 +1774,12 @@
readCompactedLongArray(in, version, mMemFactorDurations, mMemFactorDurations.length);
+ mSysMemUsageTable = readTableFromParcel(in, TAG, "sys mem usage");
+ if (mSysMemUsageTable == BAD_TABLE) {
+ return;
+ }
+ mSysMemUsageTableSize = mSysMemUsageTable != null ? mSysMemUsageTable.length : 0;
+
int NPROC = in.readInt();
if (NPROC < 0) {
mReadError = "bad process count: " + NPROC;
@@ -1826,6 +2159,10 @@
boolean dumpAll, boolean activeOnly) {
long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
+ if (mSysMemUsageTable != null) {
+ pw.println("System memory usage:");
+ dumpSysMemUsage(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
+ }
ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
boolean printedHeader = false;
boolean sepNeeded = false;
@@ -2089,10 +2426,57 @@
dumpTotalsLocked(pw, now);
}
+ long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight,
+ long totalTime, long curTotalMem, int samples) {
+ if (memWeight != 0) {
+ long mem = (long)(memWeight * 1024 / totalTime);
+ pw.print(prefix);
+ pw.print(label);
+ pw.print(": ");
+ printSizeValue(pw, mem);
+ pw.print(" (");
+ pw.print(samples);
+ pw.print(" samples)");
+ pw.println();
+ return curTotalMem + mem;
+ }
+ return curTotalMem;
+ }
+
void dumpTotalsLocked(PrintWriter pw, long now) {
pw.println("Run time Stats:");
dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now);
pw.println();
+ pw.println("Memory usage:");
+ TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
+ ALL_MEM_ADJ);
+ computeTotalMemoryUse(totalMem, now);
+ long totalPss = 0;
+ totalPss = printMemoryCategory(pw, " ", "Kernel ", totalMem.sysMemKernelWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Native ", totalMem.sysMemNativeWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ for (int i=0; i<STATE_COUNT; i++) {
+ // Skip restarting service state -- that is not actually a running process.
+ if (i != STATE_SERVICE_RESTARTING) {
+ totalPss = printMemoryCategory(pw, " ", STATE_NAMES[i],
+ totalMem.processStateWeight[i], totalMem.totalTime, totalPss,
+ totalMem.processStateSamples[i]);
+ }
+ }
+ totalPss = printMemoryCategory(pw, " ", "Cached ", totalMem.sysMemCachedWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ pw.print(" TOTAL : ");
+ printSizeValue(pw, totalPss);
+ pw.println();
+ printMemoryCategory(pw, " ", STATE_NAMES[STATE_SERVICE_RESTARTING],
+ totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss,
+ totalMem.processStateSamples[STATE_SERVICE_RESTARTING]);
+ pw.println();
pw.print(" Start time: ");
pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
pw.println();
@@ -2118,8 +2502,6 @@
}
pw.print(' ');
pw.print(mRuntime);
- pw.print(' ');
- pw.print(mWebView);
pw.println();
}
@@ -2208,7 +2590,7 @@
public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
final long now = SystemClock.uptimeMillis();
final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
- pw.println("vers,4");
+ pw.println("vers,5");
pw.print("period,"); pw.print(mTimePeriodStartClockStr);
pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
@@ -2229,7 +2611,7 @@
pw.print(",partial");
}
pw.println();
- pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView);
+ pw.print("config,"); pw.println(mRuntime);
for (int ip=0; ip<pkgMap.size(); ip++) {
final String pkgName = pkgMap.keyAt(ip);
if (reqPackage != null && !reqPackage.equals(pkgName)) {
@@ -2362,6 +2744,53 @@
pw.print("total");
dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor,
mStartTime, now);
+ if (mSysMemUsageTable != null) {
+ pw.print("sysmemusage");
+ for (int i=0; i<mSysMemUsageTableSize; i++) {
+ int off = mSysMemUsageTable[i];
+ int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ pw.print(",");
+ printProcStateTag(pw, type);
+ for (int j=SYS_MEM_USAGE_SAMPLE_COUNT; j<SYS_MEM_USAGE_COUNT; j++) {
+ if (j > SYS_MEM_USAGE_CACHED_MINIMUM) {
+ pw.print(":");
+ }
+ pw.print(getLong(off, j));
+ }
+ }
+ }
+ pw.println();
+ TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
+ ALL_MEM_ADJ);
+ computeTotalMemoryUse(totalMem, now);
+ pw.print("weights,");
+ pw.print(totalMem.totalTime);
+ pw.print(",");
+ pw.print(totalMem.sysMemCachedWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemFreeWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemZRamWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemKernelWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemNativeWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ for (int i=0; i<STATE_COUNT; i++) {
+ pw.print(",");
+ pw.print(totalMem.processStateWeight[i]);
+ pw.print(":");
+ pw.print(totalMem.processStateSamples[i]);
+ }
pw.println();
}
@@ -2452,6 +2881,15 @@
}
}
+ final public static class ProcessStateHolder {
+ public final int appVersion;
+ public ProcessStats.ProcessState state;
+
+ public ProcessStateHolder(int _appVersion) {
+ appVersion = _appVersion;
+ }
+ }
+
public static final class ProcessState extends DurationsTable {
public ProcessState mCommonProcess;
public final String mPackage;
@@ -2660,7 +3098,7 @@
* @param pkgList Processes to update.
*/
public void setState(int state, int memFactor, long now,
- ArrayMap<String, ProcessState> pkgList) {
+ ArrayMap<String, ProcessStateHolder> pkgList) {
if (state < 0) {
state = mNumStartedServices > 0
? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
@@ -2770,7 +3208,7 @@
}
public void addPss(long pss, long uss, boolean always,
- ArrayMap<String, ProcessState> pkgList) {
+ ArrayMap<String, ProcessStateHolder> pkgList) {
ensureNotDead();
if (!always) {
if (mLastPssState == mCurState && SystemClock.uptimeMillis()
@@ -2845,7 +3283,7 @@
}
}
- public void reportExcessiveWake(ArrayMap<String, ProcessState> pkgList) {
+ public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) {
ensureNotDead();
mCommonProcess.mNumExcessiveWake++;
if (!mCommonProcess.mMultiPackage) {
@@ -2857,7 +3295,7 @@
}
}
- public void reportExcessiveCpu(ArrayMap<String, ProcessState> pkgList) {
+ public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) {
ensureNotDead();
mCommonProcess.mNumExcessiveCpu++;
if (!mCommonProcess.mMultiPackage) {
@@ -2888,7 +3326,7 @@
}
}
- public void reportCachedKill(ArrayMap<String, ProcessState> pkgList, long pss) {
+ public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) {
ensureNotDead();
mCommonProcess.addCachedKill(1, pss, pss, pss);
if (!mCommonProcess.mMultiPackage) {
@@ -2925,8 +3363,10 @@
return this;
}
- private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList, int index) {
- ProcessState proc = pkgList.valueAt(index);
+ private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList,
+ int index) {
+ ProcessStateHolder holder = pkgList.valueAt(index);
+ ProcessState proc = holder.state;
if (mDead && proc.mCommonProcess != proc) {
// Somehow we are contining to use a process state that is dead, because
// it was not being told it was active during the last commit. We can recover
@@ -2959,7 +3399,7 @@
throw new IllegalStateException("Didn't create per-package process "
+ proc.mName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
}
- pkgList.setValueAt(index, proc);
+ holder.state = proc;
}
return proc;
}
@@ -3351,4 +3791,27 @@
}
}
}
+
+ public static class TotalMemoryUseCollection {
+ final int[] screenStates;
+ final int[] memStates;
+
+ public TotalMemoryUseCollection(int[] _screenStates, int[] _memStates) {
+ screenStates = _screenStates;
+ memStates = _memStates;
+ }
+
+ public long totalTime;
+ public long[] processStatePss = new long[STATE_COUNT];
+ public double[] processStateWeight = new double[STATE_COUNT];
+ public long[] processStateTime = new long[STATE_COUNT];
+ public int[] processStateSamples = new int[STATE_COUNT];
+ public long[] sysMemUsage = new long[SYS_MEM_USAGE_COUNT];
+ public double sysMemCachedWeight;
+ public double sysMemFreeWeight;
+ public double sysMemZRamWeight;
+ public double sysMemKernelWeight;
+ public double sysMemNativeWeight;
+ public int sysMemSamples;
+ }
}
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index 87a80ac..7bd316f 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -830,7 +830,9 @@
}
public boolean isShowing() {
- return mNowShowing && getHideOffset() < getHeight();
+ final int height = getHeight();
+ // Take into account the case where the bar has a 0 height due to not being measured yet.
+ return mNowShowing && (height == 0 || getHideOffset() < height);
}
void animateToMode(boolean toActionMode) {
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index ff0ee65..b098de8 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -93,6 +93,9 @@
private File mFullRestoreSetDir;
private HashSet<String> mFullRestorePackages;
+ private FileInputStream mCurFullRestoreStream;
+ private FileOutputStream mFullRestoreSocketStream;
+ private byte[] mFullRestoreBuffer;
public LocalTransport(Context context) {
mContext = context;
@@ -104,34 +107,41 @@
}
}
+ @Override
public String name() {
return new ComponentName(mContext, this.getClass()).flattenToShortString();
}
+ @Override
public Intent configurationIntent() {
// The local transport is not user-configurable
return null;
}
+ @Override
public String currentDestinationString() {
return TRANSPORT_DESTINATION_STRING;
}
+ @Override
public String transportDirName() {
return TRANSPORT_DIR_NAME;
}
+ @Override
public long requestBackupTime() {
// any time is a good time for local backup
return 0;
}
+ @Override
public int initializeDevice() {
if (DEBUG) Log.v(TAG, "wiping all data");
deleteContents(mCurrentSetDir);
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
+ @Override
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
if (DEBUG) {
try {
@@ -191,7 +201,7 @@
entity.write(buf, 0, dataSize);
} catch (IOException e) {
Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
} finally {
entity.close();
}
@@ -199,11 +209,11 @@
entityFile.delete();
}
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
} catch (IOException e) {
// oops, something went wrong. abort the operation and return error.
Log.v(TAG, "Exception reading backup input:", e);
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
}
@@ -222,6 +232,7 @@
}
}
+ @Override
public int clearBackupData(PackageInfo packageInfo) {
if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
@@ -243,9 +254,10 @@
packageDir.delete();
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
+ @Override
public int finishBackup() {
if (DEBUG) Log.v(TAG, "finishBackup()");
if (mSocket != null) {
@@ -259,24 +271,27 @@
mFullTargetPackage = null;
mSocket.close();
} catch (IOException e) {
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
} finally {
mSocket = null;
}
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
// ------------------------------------------------------------------------------------
// Full backup handling
+
+ @Override
public long requestFullBackupTime() {
return 0;
}
+ @Override
public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) {
if (mSocket != null) {
Log.e(TAG, "Attempt to initiate full backup while one is in progress");
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
if (DEBUG) {
@@ -291,7 +306,7 @@
mSocketInputStream = new FileInputStream(mSocket.getFileDescriptor());
} catch (IOException e) {
Log.e(TAG, "Unable to process socket for full backup");
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
mFullTargetPackage = targetPackage.packageName;
@@ -300,18 +315,19 @@
File tarball = new File(mCurrentSetFullDir, mFullTargetPackage);
tarstream = new FileOutputStream(tarball);
} catch (FileNotFoundException e) {
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
mFullBackupOutputStream = new BufferedOutputStream(tarstream);
mFullBackupBuffer = new byte[4096];
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
+ @Override
public int sendBackupData(int numBytes) {
if (mFullBackupBuffer == null) {
Log.w(TAG, "Attempted sendBackupData before performFullBackup");
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
if (numBytes > mFullBackupBuffer.length) {
@@ -323,21 +339,23 @@
if (nRead < 0) {
// Something went wrong if we expect data but saw EOD
Log.w(TAG, "Unexpected EOD; failing backup");
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead);
numBytes -= nRead;
} catch (IOException e) {
Log.e(TAG, "Error handling backup data for " + mFullTargetPackage);
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
// ------------------------------------------------------------------------------------
// Restore handling
static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 };
+
+ @Override
public RestoreSet[] getAvailableRestoreSets() {
long[] existing = new long[POSSIBLE_SETS.length + 1];
int num = 0;
@@ -358,11 +376,13 @@
return available;
}
+ @Override
public long getCurrentRestoreSet() {
// The current restore set always has the same token
return CURRENT_SET_TOKEN;
}
+ @Override
public int startRestore(long token, PackageInfo[] packages) {
if (DEBUG) Log.v(TAG, "start restore " + token);
mRestorePackages = packages;
@@ -371,7 +391,7 @@
mRestoreSetDir = new File(mDataDir, Long.toString(token));
mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR);
mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR);
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
@Override
@@ -397,6 +417,7 @@
if (maybeFullData.length() > 0) {
if (DEBUG) Log.v(TAG, " nextRestorePackage(TYPE_FULL_STREAM) = " + name);
mRestoreType = RestoreDescription.TYPE_FULL_STREAM;
+ mCurFullRestoreStream = null; // ensure starting from the ground state
found = true;
}
}
@@ -410,6 +431,7 @@
return RestoreDescription.NO_MORE_PACKAGES;
}
+ @Override
public int getRestoreData(ParcelFileDescriptor outFd) {
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
@@ -426,7 +448,7 @@
ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error
Log.e(TAG, "No keys for package: " + packageDir);
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
// We expect at least some data if the directory exists in the first place
@@ -447,10 +469,10 @@
in.close();
}
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
} catch (IOException e) {
Log.e(TAG, "Unable to read backup records", e);
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
}
@@ -487,38 +509,27 @@
return contents;
}
+ @Override
public void finishRestore() {
if (DEBUG) Log.v(TAG, "finishRestore()");
+ if (mRestoreType == RestoreDescription.TYPE_FULL_STREAM) {
+ resetFullRestoreState();
+ }
+ mRestoreType = 0;
}
// ------------------------------------------------------------------------------------
// Full restore handling
- public int prepareFullRestore(long token, String[] targetPackages) {
- mRestoreSetDir = new File(mDataDir, Long.toString(token));
- mFullRestoreSetDir = new File(mRestoreSetDir, FULL_DATA_DIR);
- mFullRestorePackages = new HashSet<String>();
- if (mFullRestoreSetDir.exists()) {
- List<String> pkgs = Arrays.asList(mFullRestoreSetDir.list());
- HashSet<String> available = new HashSet<String>(pkgs);
-
- for (int i = 0; i < targetPackages.length; i++) {
- if (available.contains(targetPackages[i])) {
- mFullRestorePackages.add(targetPackages[i]);
- }
- }
+ private void resetFullRestoreState() {
+ try {
+ mCurFullRestoreStream.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to close full restore input stream");
}
- return BackupTransport.TRANSPORT_OK;
- }
-
- /**
- * Ask the transport what package's full data will be restored next. When all apps'
- * data has been delivered, the transport should return {@code null} here.
- * @return The package name of the next application whose data will be restored, or
- * {@code null} if all available package has been delivered.
- */
- public String getNextFullRestorePackage() {
- return null;
+ mCurFullRestoreStream = null;
+ mFullRestoreSocketStream = null;
+ mFullRestoreBuffer = null;
}
/**
@@ -543,7 +554,79 @@
* indicating a fatal error condition that precludes further restore operations
* on the current dataset.
*/
+ @Override
public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
- return 0;
+ if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
+ throw new IllegalStateException("Asked for full restore data for non-stream package");
+ }
+
+ // first chunk?
+ if (mCurFullRestoreStream == null) {
+ final String name = mRestorePackages[mRestorePackage].packageName;
+ if (DEBUG) Log.i(TAG, "Starting full restore of " + name);
+ File dataset = new File(mRestoreSetFullDir, name);
+ try {
+ mCurFullRestoreStream = new FileInputStream(dataset);
+ } catch (IOException e) {
+ // If we can't open the target package's tarball, we return the single-package
+ // error code and let the caller go on to the next package.
+ Log.e(TAG, "Unable to read archive for " + name);
+ return TRANSPORT_PACKAGE_REJECTED;
+ }
+ mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
+ mFullRestoreBuffer = new byte[32*1024];
+ }
+
+ int nRead;
+ try {
+ nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
+ if (nRead < 0) {
+ // EOF: tell the caller we're done
+ nRead = NO_MORE_DATA;
+ } else if (nRead == 0) {
+ // This shouldn't happen when reading a FileInputStream; we should always
+ // get either a positive nonzero byte count or -1. Log the situation and
+ // treat it as EOF.
+ Log.w(TAG, "read() of archive file returned 0; treating as EOF");
+ nRead = NO_MORE_DATA;
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, " delivering restore chunk: " + nRead);
+ }
+ mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
+ }
+ } catch (IOException e) {
+ return TRANSPORT_ERROR; // Hard error accessing the file; shouldn't happen
+ } finally {
+ // Most transports will need to explicitly close 'socket' here, but this transport
+ // is in the same process as the caller so it can leave it up to the backup manager
+ // to manage both socket fds.
+ }
+
+ return nRead;
}
+
+ /**
+ * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
+ * data for restore, it will invoke this method to tell the transport that it should
+ * abandon the data download for the current package. The OS will then either call
+ * {@link #nextRestorePackage()} again to move on to restoring the next package in the
+ * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
+ * operation.
+ *
+ * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
+ * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
+ * transport-level failure. If the transport reports an error here, the entire restore
+ * operation will immediately be finished with no further attempts to restore app data.
+ */
+ @Override
+ public int abortFullRestore() {
+ if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
+ throw new IllegalStateException("abortFullRestore() but not currently restoring");
+ }
+ resetFullRestoreState();
+ mRestoreType = 0;
+ return TRANSPORT_OK;
+ }
+
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 240d520..1ccaa0f3 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -291,6 +291,7 @@
final StopwatchTimer[] mBluetoothStateTimer = new StopwatchTimer[NUM_BLUETOOTH_STATES];
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ long mMobileRadioActiveStartTime;
StopwatchTimer mMobileRadioActiveTimer;
StopwatchTimer mMobileRadioActivePerAppTimer;
LongSamplingCounter mMobileRadioActiveAdjustedTime;
@@ -1425,10 +1426,6 @@
return 0;
}
- long getLastUpdateTimeMs() {
- return mUpdateTime;
- }
-
void stopRunningLocked(long elapsedRealtimeMs) {
// Ignore attempt to stop a timer that isn't running
if (mNesting == 0) {
@@ -2790,11 +2787,11 @@
powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
|| powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
if (active) {
- realElapsedRealtimeMs = elapsedRealtime;
+ mMobileRadioActiveStartTime = realElapsedRealtimeMs = elapsedRealtime;
mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
} else {
realElapsedRealtimeMs = timestampNs / (1000*1000);
- long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+ long lastUpdateTimeMs = mMobileRadioActiveStartTime;
if (realElapsedRealtimeMs < lastUpdateTimeMs) {
Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+ " is before start time " + lastUpdateTimeMs);
diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
index 017801b..bc1f002 100644
--- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl
+++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
@@ -23,6 +23,7 @@
void startUi(IBinder containingWindowToken, int x, int y, int width, int height,
boolean useLiveliness);
void stopUi();
+ void startWithoutUi();
void registerCallback(IFaceLockCallback cb);
void unregisterCallback(IFaceLockCallback cb);
}
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..935e3a0 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_);
}
@@ -1267,13 +1320,9 @@
(void*) SkCanvasGlue::drawTextRun___CIIIIFFZPaintTypeface},
{"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V",
(void*) SkCanvasGlue::drawTextRun__StringIIIIFFZPaintTypeface},
- {"native_drawPosText","(J[CII[FJ)V",
- (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/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index d07b154..dc30814 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -37,6 +37,7 @@
#include "TextLayout.h"
#ifdef USE_MINIKIN
+#include <minikin/GraphemeBreak.h>
#include <minikin/Layout.h>
#include "MinikinSkia.h"
#include "MinikinUtils.h"
@@ -778,6 +779,11 @@
static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
jint count, jint flags, jint offset, jint opt) {
+#ifdef USE_MINIKIN
+ GraphemeBreak::MoveOpt moveOpt = GraphemeBreak::MoveOpt(opt);
+ size_t result = GraphemeBreak::getTextRunCursor(text, start, count, offset, moveOpt);
+ return static_cast<jint>(result);
+#else
jfloat scalarArray[count];
TextLayout::getTextRunAdvances(paint, text, start, count, start + count, flags,
@@ -818,6 +824,7 @@
}
return pos;
+#endif
}
static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
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_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 3a53331..4c9feca 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -466,7 +466,7 @@
// connect to camera service
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jint cameraId, jstring clientPackageName)
+ jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
// Convert jstring to String16
const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
@@ -474,8 +474,18 @@
String16 clientName(rawClientName, rawClientNameLen);
env->ReleaseStringChars(clientPackageName, rawClientName);
- sp<Camera> camera = Camera::connect(cameraId, clientName,
- Camera::USE_CALLING_UID);
+ sp<Camera> camera;
+ if (halVersion == ICameraService::CAMERA_HAL_API_VERSION_UNSPECIFIED) {
+ // Default path: hal version is unspecified, do normal camera open.
+ camera = Camera::connect(cameraId, clientName,
+ Camera::USE_CALLING_UID);
+ } else {
+ jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
+ Camera::USE_CALLING_UID, camera);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
if (camera == NULL) {
return -EACCES;
@@ -510,7 +520,6 @@
// finalizer is invoked later.
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
{
- // TODO: Change to ALOGV
ALOGV("release camera");
JNICameraContext* context = NULL;
sp<Camera> camera;
@@ -891,7 +900,7 @@
"(ILandroid/hardware/Camera$CameraInfo;)V",
(void*)android_hardware_Camera_getCameraInfo },
{ "native_setup",
- "(Ljava/lang/Object;ILjava/lang/String;)I",
+ "(Ljava/lang/Object;IILjava/lang/String;)I",
(void*)android_hardware_Camera_native_setup },
{ "native_release",
"()V",
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..dc1ea06 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);
}
@@ -795,49 +840,6 @@
env->ReleaseStringChars(text, textArray);
}
-static void renderPosText(OpenGLRenderer* renderer, const jchar* text, int count,
- const jfloat* positions, jint bidiFlags, SkPaint* paint) {
- sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
- text, 0, count, count, bidiFlags);
- if (value == NULL) {
- return;
- }
- const jchar* glyphs = value->getGlyphs();
- size_t glyphsCount = value->getGlyphsCount();
- if (count < int(glyphsCount)) glyphsCount = count;
- int bytesCount = glyphsCount * sizeof(jchar);
-
- renderer->drawPosText((const char*) glyphs, bytesCount, glyphsCount, positions, paint);
-}
-
-static void android_view_GLES20Canvas_drawPosTextArray(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jcharArray text, jint index, jint count,
- jfloatArray pos, jlong paintPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- jchar* textArray = env->GetCharArrayElements(text, NULL);
- jfloat* positions = env->GetFloatArrayElements(pos, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
-
- renderPosText(renderer, textArray + index, count, positions, kBidi_LTR, paint);
-
- env->ReleaseFloatArrayElements(pos, positions, JNI_ABORT);
- env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
-}
-
-static void android_view_GLES20Canvas_drawPosText(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jstring text, jint start, jint end,
- jfloatArray pos, jlong paintPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- const jchar* textArray = env->GetStringChars(text, NULL);
- jfloat* positions = env->GetFloatArrayElements(pos, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
-
- renderPosText(renderer, textArray + start, end - start, positions, kBidi_LTR, paint);
-
- env->ReleaseFloatArrayElements(pos, positions, JNI_ABORT);
- env->ReleaseStringChars(text, textArray);
-}
-
// ----------------------------------------------------------------------------
// Display lists
// ----------------------------------------------------------------------------
@@ -986,18 +988,14 @@
{ "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 },
{ "nDrawTextRun", "(JLjava/lang/String;IIIIFFZJJ)V",
(void*) android_view_GLES20Canvas_drawTextRun },
- { "nDrawPosText", "(J[CII[FJ)V", (void*) android_view_GLES20Canvas_drawPosTextArray },
- { "nDrawPosText", "(JLjava/lang/String;II[FJ)V",
- (void*) android_view_GLES20Canvas_drawPosText },
-
{ "nGetClipBounds", "(JLandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_getClipBounds },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c0d5221..cfc8eb8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -61,6 +61,8 @@
jfieldID xDpi;
jfieldID yDpi;
jfieldID secure;
+ jfieldID appVsyncOffsetNanos;
+ jfieldID presentationDeadlineNanos;
} gPhysicalDisplayInfoClassInfo;
static struct {
@@ -392,6 +394,10 @@
env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi);
env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi);
env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure);
+ env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos,
+ info.appVsyncOffset);
+ env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos,
+ info.presentationDeadline);
env->SetObjectArrayElement(configArray, static_cast<jsize>(c), infoObj);
env->DeleteLocalRef(infoObj);
}
@@ -648,6 +654,10 @@
gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F");
gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F");
gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z");
+ gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos = env->GetFieldID(clazz,
+ "appVsyncOffsetNanos", "J");
+ gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = env->GetFieldID(clazz,
+ "presentationDeadlineNanos", "J");
jclass rectClazz = env->FindClass("android/graphics/Rect");
gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I");
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/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8b5dff0..fa1a563 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2742,7 +2742,8 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:multiprocess="true"
- android:documentLaunchMode="never">
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true">
<intent-filter>
<action android:name="android.intent.action.CHOOSER" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index 7e212be..ace17ae 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:detachWallpaper="true" android:shareInterpolator="false" android:startOffset="60">
+ android:detachWallpaper="true" android:shareInterpolator="false" android:startOffset="100">
<alpha
android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true"
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
index 5325712..16c101b 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -15,20 +15,29 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" >
<size
- android:width="19.0dp"
- android:height="19.0dp"/>
+ android:width="20.0dp"
+ android:height="20.0dp"/>
<viewport
- android:viewportWidth="19.0"
- android:viewportHeight="19.0"/>
+ android:viewportWidth="20.0"
+ android:viewportHeight="20.0"/>
<path
- android:pathData="M9.5,9.5m-9.5,0.0a9.5,9.5 0.0,1.0 1.0,19.0 0.0a9.5,9.5 0.0,1.0 1.0,-19.0 0.0"
+ 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="M12.667,7.125l-1.583,0.0L11.084,6.333l-0.792,-0.792L8.708,5.5410004L7.917,6.333l0.0,0.792L6.333,7.125c-0.438,0.0 -0.788,0.354 -0.788,0.792l-0.004,4.354c0.0,0.438 0.354,0.792 0.792,0.792l6.333,0.0c0.438,0.0 0.792,-0.354 0.792,-0.792L13.458,7.917C13.458,7.479 13.104,7.125 12.667,7.125zM10.094,10.687L8.906,10.687L8.906,9.5l1.188,0.0L10.094,10.687zM10.292,7.125L8.708,7.125L8.708,6.333l1.583,0.0L10.291,7.125z"
+ 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="M4.75,4.75 h9.5 v9.5 h-9.5z"
+ 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
index 7bfab4c..c8e49e1 100644
--- a/core/res/res/drawable/ic_corp_icon_badge.xml
+++ b/core/res/res/drawable/ic_corp_icon_badge.xml
@@ -24,17 +24,31 @@
<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: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: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="M53.667,45.5l-2.333,0.0l0.0,-1.167l-1.167,-1.167l-2.333,0.0l-1.167,1.167L46.667,45.5l-2.333,0.0c-0.645,0.0 -1.161,0.522 -1.161,1.167l-0.006,6.417c0.0,0.645 0.522,1.167 1.167,1.167l9.333,0.0c0.645,0.0 1.167,-0.522 1.167,-1.167l0.0,-6.417C54.833,46.022 54.311,45.5 53.667,45.5zM49.875,50.75l-1.75,0.0L48.125,49.0l1.75,0.0L49.875,50.75zM50.167,45.5l-2.333,0.0l0.0,-1.167l2.333,0.0L50.167,45.5z"
+ 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="M42.0,42.0 h14.0 v14.0 h-14.0z"
+ 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-af/strings.xml b/core/res/res/values-af/strings.xml
index 72b0909..cb93530 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Laat die program toe om \'n alarm in \'n geïnstalleerde wekkerprogram te stel. Sommige wekkerprogramme werk dalk nie met hierdie funksie nie."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"voeg stemboodskap by"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Laat die program toe om boodskappe by te voeg by jou stempos-inkassie."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"lees alle stempos"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Laat die program toe om al jou stemposse te lees."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"verander blaaier se geoligging-toestemmings"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Laat die program toe om die blaaier se geoligging-toestemmings te verander. Kwaadwillige programme kan dit gebruik om hulle toe te laat om ligginginligting aan enige webwerf te stuur."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifieer pakkies"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Laat \'n program toe om vir veranderinge in vertrouenstaat te luister."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Voorsien \'n vertroude agent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Laat \'n program toe om \'n vertroude agent te voorsien."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Begin vertrouensagente se instellingskieslys."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Laat \'n program toe om \'n aktiwiteit te begin wat die vertrouensagent se gedrag verander."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Verbind met \'n vertrouensagentdiens"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Laat \'n program toe om met \'n vertrouensagentdiens te verbind."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Tree in wisselwerking met opdatering- en terugstellingstelsel"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index da89136..66faf61 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"በተጫነው የማንቂያ ሰዓት መተግበሪያ ውስጥ ማንቅያን ለማደራጀት ለመተግበሪያው ይፈቅዳሉ፡፡አንዳንድ የማንቂያ ሰዓት መተግበሪያዎች ይሄንን ባህሪ ላይፈፅሙ ይችላሉ፡፡"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"የድምፅ መልዕክት አክል"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ወደ ድምፅ መልዕክት የገቢ መልዕክትህ መልዕክቶች ለማከል ለመተግበሪያው ይፈቅዳሉ።"</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"ሁሉንም የድምጽ መልዕክት ያነብባል"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"መተግበሪያው ሁሉንም የድምጽ መልዕክቶችዎ እንዲያነባቸው ያስችለዋል።"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"የአሳሽ ገፀ ሥፍራ ፍቃዶችን ቀይር"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"የአሳሹን የጂኦ-አካባቢ ፍቃዶችን እንዲለውጥ ለመተግበሪያው ይፈቅዳል፡፡ተንኮል አዘል መተግበሪያዎች የመላኪያ አከባቢን መረጃ ወደ አጠራጣሪ የድር ጣቢያዎች ለመፍቀድ ይሄንን ሊጠቀሙበት ይችላሉ፡፡"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ፓኬጆችን አረጋግጥ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 626858f..7d1117b 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"للسماح للتطبيق بضبط المنبه في تطبيق المنبه المثبّت. ربما لا تنفذ بعض تطبيقات المنبه هذه الميزة."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"إضافة بريد صوتي"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"للسماح للتطبيق بإضافة رسائل إلى صندوق البريد الصوتي."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"قراءة جميع رسائل البريد الصوتي"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"للسماح للتطبيق بقراءة جميع رسائل البريد الصوتي."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تعديل أذونات الموقع الجغرافي للمتصفح"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"للسماح لأحد التطبيقات بتعديل أذونات الموقع الجغرافي للمتصفح. يمكن أن تستخدم التطبيقات الضارة هذا للسماح بإرسال معلومات الموقع إلى مواقع ويب عشوائية."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"التحقق من الحزم"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index ca71ce3..b9c4f46 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -189,7 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Безопасен режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Системно от Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Лични приложения"</string>
- <string name="managed_profile_label" msgid="4287077106125758391">"Служебен под Android"</string>
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Услуги, които ви струват пари"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Извършват неща, които могат да ви струват пари."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Вашите съобщения"</string>
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Разрешава на приложението да навие инсталирано приложение будилник. Някои будилници може да не изпълнят тази функция."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"добавяне на гласова поща"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Разрешава на приложението да добавя съобщения към входящата ви гласова поща."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"промяна на разрешенията за местоположение в браузъра"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Разрешава на приложението да променя разрешенията на браузъра за местоположение. Злонамерените приложения могат да използват това, за да изпращат информация за местоположението до произволни уебсайтове."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"проверка на пакетите"</string>
@@ -1362,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Разрешава на приложението да следи за промени в състоянието на надеждност."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Предоставяне на trust agent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Разрешава на приложението да предоставя trust agent."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Стартиране на менюто за настройки за trust agent."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Разрешава на приложението да стартира активност, която променя поведението на trust agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Обвързване с услуга за trust agents"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Разрешава на приложението да се обвърже с услуга за trust agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Взаимодействие със системата за актуализации и възстановяване"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e1eed49..18389b6a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -265,8 +265,8 @@
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
<string name="permlab_statusBarService" msgid="7247281911387931485">"barra d\'estat"</string>
<string name="permdesc_statusBarService" msgid="716113660795976060">"Permet que l\'aplicació sigui la barra d\'estat."</string>
- <string name="permlab_expandStatusBar" msgid="1148198785937489264">"ampliar/reduir la barra d\'estat"</string>
- <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permet que l\'aplicació ampliï o redueixi la barra d\'estat."</string>
+ <string name="permlab_expandStatusBar" msgid="1148198785937489264">"desplega/contrau la barra d\'estat"</string>
+ <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permet que l\'aplicació desplegui o replegui la barra d\'estat."</string>
<string name="permlab_install_shortcut" msgid="4279070216371564234">"instal·la dreceres"</string>
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"Permet que una aplicació afegeixi dreceres a la pantalla d\'inici sense la intervenció de l\'usuari."</string>
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"desinstal·la dreceres"</string>
@@ -945,7 +945,7 @@
<string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"S\'ha iniciat la reorganització del widget."</string>
<string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Ha finalitzat la reorganització del widget."</string>
<string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"S\'ha suprimit el widget de <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
- <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Amplia l\'àrea de desbloqueig."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Desplega l\'àrea de desbloqueig."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueig lliscant el dit"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueig mitjançant patró"</string>
<string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueig facial"</string>
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permet que l\'aplicació defineixi una alarma en una aplicació de despertador instal·lada. És possible que algunes aplicacions de despertador no incorporin aquesta funció."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"afegeix bústia de veu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet que l\'aplicació afegeixi missatges a la safata d\'entrada de la bústia de veu."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"accedir a tots els correus de veu"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Permet que l\'aplicació accedeixi a tots els teus correus de veu."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modifica els permisos d\'ubicació geogràfica del navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet que l\'aplicació modifiqui els permisos d\'ubicació geogràfica del navegador. Les aplicacions malicioses poden utilitzar-ho per enviar la informació d\'ubicació a llocs web arbitraris."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifica paquets"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permet que una aplicació escolti els canvis en l\'estat de confiança."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Proporcionar un agent de confiança"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permet que una aplicació proporcioni un agent de confiança."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Inicia el menú de configuració de l\'agent de confiança."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permet que una aplicació iniciï una activitat que canviï el comportament de l\'agent de confiança."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Enllaçar amb el servei d\'un agent de confiança"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permet que una aplicació es vinculi amb el servei d\'un agent de confiança."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interacciona amb el sistema de recuperació i amb les actualitzacions"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cf06dc1..aeb19d0 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Umožňuje aplikaci nastavit budík v nainstalované aplikaci budík. Některé aplikace budík tuto funkci nemusí obsahovat."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"přidat hlasovou zprávu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Umožňuje aplikaci přidávat zprávy do hlasové schránky."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"číst všechny hlasové zprávy"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Umožňuje aplikaci číst všechny vaše hlasové zprávy."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"změna oprávnění prohlížeče poskytovat informace o zeměpisné poloze"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Umožňuje aplikaci upravit oprávnění funkce geolokace v prohlížeči. Škodlivé aplikace toho mohou využít k odeslání údajů o poloze na libovolné webové stránky."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ověřit balíčky"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Umožňuje aplikaci naslouchat změnám ve stavu důvěryhodnosti."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Poskytování zástupce důvěryhodnosti"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Umožňuje aplikaci poskytnout zástupce důvěryhodnosti."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Spustit nabídku nastavení agenta důvěryhodnosti"</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Umožňuje aplikaci spustit aktivitu, která změní chování agenta důvěryhodnosti."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Vázat se na službu zástupce důvěryhodnosti"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Umožňuje aplikaci vázat se na službu zástupce důvěryhodnosti."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakce se systémem aktualizací a obnovení"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0aa2a36..2996f75 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Tillader, at appen kan indstille en alarm i en installeret alarmapp. Nogle alarmapps har muligvis ikke denne funktion."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføje telefonsvarer"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Tillader, at appen kan tilføje beskeder på din telefonsvarer."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"læs alle talebeskeder"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Tillader, at appen kan læse alle dine talebeskeder"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"skifte tilladelser til geografisk placering i Browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websites."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"bekræft pakker"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index d557d37..fe5c1390 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Ermöglicht der App, einen Alarm in einer installierten Wecker-App einzurichten. Einige Wecker-Apps implementieren diese Funktion möglicherweise nicht."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"Mailbox-Nachrichten hinzufügen"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ermöglicht der App, Nachrichten zu Ihrem Mailbox-Posteingang hinzuzufügen"</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"Alle Mailboxnachrichten abrufen"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Ermöglicht der App das Abrufen aller Ihrer Mailboxnachrichten"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Geolokalisierungsberechtigungen des Browsers ändern"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ermöglicht der App, die Geolokalisierungsberechtigungen des Browsers zu ändern. Schädliche Apps können so Standortinformationen an beliebige Websites senden."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Pakete überprüfen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8205c60..1a70d19 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Επιτρέπει στην εφαρμογή τη ρύθμιση μιας ειδοποίησης σε μια εγκατεστημένη εφαρμογή ξυπνητηριού. Ορισμένες εφαρμογές ξυπνητηριού ενδέχεται να μην μπορούν να ενσωματώσουν αυτήν τη λειτουργία."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"προσθήκη τηλεφωνητή"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Επιτρέπει στην εφαρμογή να προσθέτει μηνύματα στα εισερχόμενα του αυτόματου τηλεφωνητή σας."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"ανάγνωση όλων των μηνυμάτων του αυτόματου τηλεφωνητή"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Επιτρέπει στην εφαρμογή να διαβάσει όλα τα μηνύματα του αυτόματου τηλεφωνητή σας."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"τροποποίηση δικαιωμάτων γεωγραφικής θέσης του Προγράμματος περιήγησης"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Επιτρέπει στην εφαρμογή την τροποποίηση των αδειών γεωτοποθεσίας του Προγράμματος περιήγησης. Τυχόν κακόβουλες εφαρμογές ενδέχεται να το χρησιμοποιήσουν για να επιτρέψουν την αποστολή πληροφοριών τοποθεσίας σε αυθαίρετους ιστότοπους."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"επαλήθευση πακέτων"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index a0c35c8..c659999 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Allows the app to set an alarm in an installed alarm clock app. Some alarm clock apps may not implement this feature."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"add voicemail"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Allows the app to add messages to your voicemail inbox."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"read all voicemail"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Allows the app to read all your voicemails."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modify Browser geo-location permissions"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Allows the app to modify the Browser\'s geo-location permissions. Malicious apps may use this to allow sending location information to arbitrary websites."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verify packages"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index a0c35c8..c659999 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Allows the app to set an alarm in an installed alarm clock app. Some alarm clock apps may not implement this feature."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"add voicemail"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Allows the app to add messages to your voicemail inbox."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"read all voicemail"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Allows the app to read all your voicemails."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modify Browser geo-location permissions"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Allows the app to modify the Browser\'s geo-location permissions. Malicious apps may use this to allow sending location information to arbitrary websites."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verify packages"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b6a7667..b1108bf2 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que la aplicación establezca una alarma en una aplicación de alarma instalada. Es posible que algunas aplicaciones de alarma no incluyan esta función."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"agregar correo de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que la aplicación agregue mensajes a la bandeja de entrada de tu buzón de voz."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modificar los permisos de ubicación geográfica del navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que la aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones maliciosas pueden utilizar esto para permitir el envío de información de ubicación a sitios web arbitrarios."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Verificar paquetes"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 1dcbf62..f47f5c0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que la aplicación establezca una alarma en una aplicación de reloj instalada. Es posible que algunas aplicaciones de reloj no incluyan esta función."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"añadir buzón de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que la aplicación añada mensajes a la bandeja de entrada del buzón de voz."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"consultar todos los mensajes de voz"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Permite que la aplicación consulte todos tus mensajes de voz."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificar los permisos de ubicación geográfica del navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que la aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones malintencionadas pueden usar este permiso para autorizar el envío de información sobre la ubicación a sitios web arbitrarios."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar paquetes"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 60d036e..0df3aca 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Võimaldab rakendusel seada installitud äratuskellarakenduses äratuse. Mõned äratuskellarakendused ei pruugi seda funktsiooni juurutada."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"lisa kõneposti"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Võimaldab rakendusel lisada sõnumeid teie kõneposti postkasti."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"kogu kõneposti lugemine"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Võimaldab rakendusel kogu kõneposti lugeda."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Brauseri geolokatsiooniõiguste muutmine"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Võimaldab rakendusel muuta brauseri geolokatsiooniõigusi. Pahatahtlikud rakendused võivad seda kasutada asukohateabe saatmise lubamiseks suvalistele veebisaitidele."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakettide kinnitamine"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b4727bb..d652544 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"به برنامه اجازه میدهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامههای ساعت زنگدار نمیتوانند این ویژگی را اعمال کنند."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"افزودن پست صوتی"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه میدهد تا پیامها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"خواندن کل پست صوتی"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"به برنامه اجازه میدهد همه پستهای صوتیتان را بخواند."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"به برنامه اجازه میدهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامههای مخرب میتوانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایتهای وب کتابخانه بفرستند."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"تأیید بستهها"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"به یک برنامه کاربردی برای گوش دادن به تغییرات در trust اجازه میدهد."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"یک عامل مورد اعتماد فراهم میآورد."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"به برنامه امکان میدهد یک عامل مورد اعتماد فراهم آورد."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"منوی تنظیمات نماینده امانی را راهاندازی کنید."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"به برنامه اجازه میدهد تا فعالیتی را راهاندازی کند که رفتار نماینده امانی را تغییر میدهد."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"اتصال به یک سرویس trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"به یک برنامه کاربردی برای اتصال به یک سرویس trust agent اجازه میدهد."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"تعامل با سیستم بهروزرسانی و بازیابی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 02c6da4..802fb24 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-järjestelmä"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Omat sovellukset"</string>
- <!-- no translation found for managed_profile_label (4287077106125758391) -->
- <skip />
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Maksulliset palvelut"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Suorita mahdollisesti maksullisia toimintoja."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Omat viestit"</string>
@@ -1002,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Antaa sovelluksen asettaa hälytyksen sisäiseen herätyskellosovellukseen. Jotkin herätyskellosovellukset eivät välttämättä käytä tätä ominaisuutta."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"lisää vastaajaviesti"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Antaa sovelluksen lisätä viestejä saapuneisiin vastaajaviesteihin."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"selaimen maantieteellisen sijainnin lupien muokkaaminen"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Antaa sovelluksen muokata Selaimen maantieteellisen sijainnin lupia. Haitalliset sovellukset voivat sallia tällä sijaintitietojen lähettämisen mielivaltaisiin sivustoihin."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vahvista paketteja"</string>
@@ -1363,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Antaa sovelluksen seurata luottamuksen tilamuutoksia."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Luotettavan tahon tarjoaminen"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Antaa sovelluksen tarjota luotettavan tahon."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Käynnistä luotettavan tahon asetusvalikko."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Sallii sovelluksen käynnistää toiminnon, joka muuttaa luotettavan tahon toimintaa."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Luotettavaan tahoon sitoutuminen"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Antaa sovelluksen sitoutua luotettavaan tahoon."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Vuorovaikutus päivitys- ja palautusjärjestelmän kanssa"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index b1292b3..ec4c2d6 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permet à l\'application de régler la sonnerie d\'une fonction de réveil installée sur votre appareil. Cette fonctionnalité n\'est pas compatible avec toutes les applications de réveils."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ajouter des messages vocaux"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet à l\'application d\'ajouter des messages à votre messagerie vocale."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifier les autorisations de géolocalisation du navigateur"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet à l\'application de modifier les autorisations de géolocalisation du navigateur. Des applications malveillantes peuvent exploiter cette fonctionnalité pour permettre l\'envoi de données de localisation à des sites Web arbitraires."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vérifier les paquets"</string>
@@ -1362,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permet à une application de détecter les modifications de l\'état de confiance."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fournir un agent de confiance."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permet à une application de fournir un agent de confiance."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lancer le menu des paramètres de l\'agent de confiance."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permet à une application de lancer une activité qui modifie le comportement de l\'agent de confiance."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Lier à un service d\'agent de confiance"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permet à une application de se lier à un service d\'agent de confiance."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir avec le système de récupération et de mise à jour"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f511df0..57ee762 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permet à l\'application de régler la sonnerie d\'un réveil installé. Cette fonctionnalité n\'est pas disponible sur tous les réveils."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ajouter un message vocal"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet à l\'application d\'ajouter des messages à votre messagerie vocale."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"accéder à tous les messages vocaux"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Permet à l\'application d\'accéder à tous vos messages vocaux."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifier les autorisations de géolocalisation du navigateur"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet à l\'application de modifier les autorisations de géolocalisation du navigateur. Des applications malveillantes peuvent exploiter cette fonctionnalité pour permettre l\'envoi de données de localisation à des sites Web arbitraires."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vérifier les packages"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permettre à une application de détecter les modifications de l\'état de confiance."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fournir un agent de confiance"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permettre à une application de fournir un agent de confiance"</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lancer le menu des paramètres de l\'agent de confiance"</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permet à une application de lancer une activité qui modifie le comportement de l\'agent de confiance."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"S\'associer à un service d\'agent de confiance"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permettre à une application de s\'associer à un service d\'agent de confiance."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir avec le système de récupération et de mise à jour"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 470ec41..8c2c025 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"ऐप्स को इंस्टॉल किए गए अलार्म घड़ी ऐप्स में अलार्म सेट करने देता है. हो सकता है कुछ अलार्म घड़ी ऐप्स में यह सुविधा न हो."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ध्वनिमेल जोड़ें"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ऐप्स को आपके ध्वनिमेल इनबॉक्स में संदेश जोड़ने देता है."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ब्राउज़र भौगोलिक-स्थान अनुमतियों को बदलें"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ऐप्स को ब्राउज़र के भौगोलिक-स्थान की अनुमतियां संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स इसका उपयोग एकपक्षीय वेबसाइट को स्थान जानकारी भेजने में कर सकते हैं."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"पैकेज सत्यापित करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 7dc8e19..65e64ec 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Omogućuje aplikaciji postavljanje alarma na instaliranoj aplikaciji budilici. Neke aplikacije budilice možda neće primijeniti tu značajku."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"dodaj govornu poštu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Omogućuje aplikaciji da doda poruke u vašu govornu poštu."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"čitanje svih poruka u govornoj pošti"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Aplikaciji omogućuje čitanje svih vaših poruka u govornoj pošti."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"izmjena dozvola za geolociranje u pregledniku"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Omogućuje aplikaciji promjenu geolokacijskih dozvola preglednika. Zlonamjerne aplikacije mogu to upotrijebiti da bi dopustile slanje podataka o lokaciji nasumičnim web-lokacijama."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"provjeri pakete"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Omogućuje aplikaciji praćenje promjena pouzdanog stanja."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Pružanje agenta za pouzdanost."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Omogućuje aplikaciji pružanje agenta za pouzdanost."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Pokretanje izbornika postavki pouzdanog agenta."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Aplikaciji omogućuje pokretanje aktivnosti koja mijenja ponašanje pouzdanog agenta."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Povezivanje s uslugom pouzdanog predstavnika"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Omogućuje aplikaciji povezivanje s uslugom pouzdanog predstavnika."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakcija s ažuriranjem i sustavom za oporavak"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 2582737..498e28a 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -401,7 +401,7 @@
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"csatlakozás egy hangvezérlőhöz"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Lehetővé teszi a használó számára, hogy csatlakozzon egy hangvezérlő szolgáltatás legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
<string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"hangalapú kulcskifejezések kezelése"</string>
- <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"A használó kezelheti a hangalapú hotwordök felismerésére szolgáló kulcskifejezéseket. Az átlagos alkalmazásoknak nem lehet rá szükségük."</string>
+ <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Az engedély birtokosa kezelheti a hangalapú hotwordök felismerésére szolgáló kulcskifejezéseket. Az átlagos alkalmazásoknak nem lehet rá szükségük."</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"csatlakozás egy távoli kijelzőhöz"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Lehetővé teszi a használó számára, hogy csatlakozzon egy távoli kijelző legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"csatlakozás modulszolgáltatáshoz"</string>
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Lehetővé teszi az alkalmazás számára, hogy ébresztőt állítson be egy telepített ébresztőóra alkalmazásban. Egyes ilyen alkalmazásokban lehet, hogy nem működik ez a funkció."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"hangposta hozzáadása"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Lehetővé teszi az alkalmazás számára, hogy üzeneteket adjon hozzá bejövő hangpostájához."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"az összes hangüzenet olvasása"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Engedélyezi az alkalmazásnak az összes hangüzenet olvasását."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"a böngésző helymeghatározási engedélyeinek módosítása"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a böngésző helymeghatározási engedélyeit. Rosszindulatú alkalmazások ezt arra használhatják, hogy a helyére vonatkozó információkat küldjenek tetszőleges webhelyeknek."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"csomagok ellenőrzése"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index f0bee2b..97e9ffc1 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Թույլ է տալիս հավելվածին սահմանել զարթուցիչի ծրագրում տեղադրված ազդանշանը: Զարթուցիչի որոշ հավելվածներ չեն կարող կիրառել այս հատկությունը:"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ավելացնել ձայնային փոստ"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Թույլ է տալիս հավելվածին ավելացնել հաղորդագրություններ ձեր ձայնային փոստի արկղում:"</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկչի աշխարհագրական տեղանքի թույլտվությունները"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել զննարկչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"հաստատել փաթեթները"</string>
@@ -1362,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Ծրագրին թույլ է տալիս լսել վստահության կարգավիճակի փոփոխությունները:"</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Տրամադրել վստահելի գործակալ:"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ծրագրին թույլ է տալիս տրամադրել վստահելի գործակալ:"</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Գործարկել վստահելի գործակալի կարգավորումների ցանկը:"</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Ծրագրին թույլ է տալիս իրականացնել այնպիսի գործունեություն, որը փոխում է վստահելի գործակալի վարքագիծը:"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Կապվել վստահելի գործակալի ծառայությանը"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ծրագրին թույլ է տալիս կապվել վստահելի գործակալի ծառայությանը:"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Փոխազդել թարմացման և վերականգնման համակարգի հետ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 9dca8a5..1c80430 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Mengizinkan apl menyetel alarm di apl jam alarm yang terpasang. Beberapa apl jam alarm mungkin tidak menerapkan fitur ini."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"tambahkan kotak pesan"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Mengizinkan apl menambahkan pesan ke kotak masuk untuk pesan suara Anda."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"baca semua kotak pesan"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Mengizinkan aplikasi membaca semua kotak pesan Anda."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"memodifikasi izin geolokasi Browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Mengizinkan apl memodifikasi izin geolokasi Browser. Apl berbahaya dapat menggunakan izin ini untuk memungkinkan pengiriman informasi lokasi ke sembarang situs web."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifikasi paket"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 0ea6f6e..d7b7748 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Consente all\'applicazione di impostare un allarme in un\'applicazione sveglia installata. È possibile che alcune applicazioni sveglia non possano implementare questa funzione."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"aggiunta di un messaggio vocale"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Consente all\'applicazione di aggiungere messaggi alla casella della segreteria."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"accesso alla segreteria"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Consente all\'app di accedere alla segreteria."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifica delle autorizzazioni di localizzazione geografica del browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Consente all\'applicazione di modificare le autorizzazioni di geolocalizzazione del Browser. Le applicazioni dannose potrebbero farne uso per consentire l\'invio di informazioni sulla posizione a siti web arbitrari."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifica dei pacchetti"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 19fb05a..8b33339 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"מאפשר לאפליקציה להגדיר התראה באפליקציה מותקנת של שעון מעורר. אפליקציות מסוימות של שעון מעורר אינן מיישמות תכונה זו."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"הוסף דואר קולי"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"מאפשר לאפליקציה להוסיף הודעות לתיבת הדואר הקולי."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"קריאת כל הדואר הקולי"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"מאפשרת לאפליקציה לקרוא את כל הודעות הדואר הקולי שלך."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"שינוי הרשאות המיקום הגיאוגרפי של הדפדפן"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"מאפשר לאפליקציה לשנות את הרשאות המיקום הגיאוגרפי של הדפדפן. אפליקציות זדוניות עלולות להשתמש בכך כדי לאפשר משלוח של פרטי מיקום לאתרים זדוניים אחרים."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"אימות חבילות"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 6ba99bf..a4b95a6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"インストール済みアラームアプリのアラームを設定することをアプリに許可します。この機能が実装されていないアラームアプリもあります。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ボイスメールの追加"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ボイスメール受信トレイにメッセージを追加することをアプリに許可します。"</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ブラウザの現在地情報に対する権限の変更"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ブラウザの現在地情報に対する権限の変更をアプリに許可します。この許可を悪意のあるアプリに利用されると、任意のウェブサイトに現在地情報が送信される恐れがあります。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"パッケージのベリファイ"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 879d00f..694e1c7 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -401,7 +401,7 @@
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"ხმის ინტერაქტორთან შეკავშირება"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"მფლობელს შეეძლება შეკავშირდეს ხმის ინტერაქციის სერვისების ზედა დონის ინტერფეისთან. ჩვეულებრივ აპს ეს წესით არასოდეს უნდა დასჭირდეს."</string>
<string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"ხმოვანი საიდუმლო ფრაზების მართვა"</string>
- <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"საშუალებას აძლევს მფლობელს მართოს საიდუმლო ფრაზები ხმოვანი ჯადოსნური სიტყვის ამოცნობისათვის. ეს ჩვეულებრივ აპებს არ უნდა დაჭირდეს."</string>
+ <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"საშუალებას აძლევს მფლობელს მართოს საიდუმლო ფრაზები ხმოვანი ჯადოსნური სიტყვის ამოცნობისათვის. ეს ჩვეულებრივ აპებს არ უნდა დასჭირდეს."</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"დისტანციურ მონიტორზე მიბმა"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"მფლობელს შეეძლება მიებას დისტანციურ მონიტორის ზედა დონის ინტერფეისს. ჩვეულებრივ აპს ეს წესით არასოდეს უნდა დაჭირდეს."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ვიჯეტ სერვისთან დაკავშირება"</string>
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"აპს შეეძლება მაღვიძარას დაყენება დაინსტალირებული მაღვიძარას აპლიკაციაში. ამ ფუნქციას მაღვიძარას ზოგიერთი აპი არ იყენებს."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ხმოვანი ფოსტის დამატება"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"აპს შეეძლება დაამატოს შეტყობინებები თქვენი ხმოვანი ფოსტის შემოსულებში."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ბრაუზერის გეოლოკაციის უფლებების შეცვლა"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"აპს შეეძლება ბრაუზერის გეოლოკაციის უფლებების შეცვლა. მავნე აპებმა ეს შესაძლოა გამოიყენონ ნებისმიერი ვებსაიტისთვის მდებარეობის შესახებ ინფორმაციის გასაგზავნად."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"პაკეტების გადამოწმება"</string>
@@ -1362,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"საშუალებას აძლევს აპლიკაციას მოუსმინოს ცვლილებებს სანდო მდგომარეობაში."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"სანდო აგენტის წარმოდგენა."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"საშუალებას აძლევს აპლიკაციას წარმოადგინოს სანდო აგენტი."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"ნდობის აგენტის პარამეტრების მენიუს გამოძახება."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"საშუალებას აძლევს აპლიკაციას გამოიძახოს აქტივობა, რაც ნდობის აგენტის ქცევას ცვლის."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"სანდო აგენტის სერვისზე მიმაგრება."</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"საშუალებას აძლევს აპლიკაციას მიემაგროს სანდო აგენტის სერვისს."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"განახლებასთან და აღდგენის სისტემასთან ინტერაქცია"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index daf004c..421ff10 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -68,7 +68,7 @@
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
- <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string>
+ <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string>
<string name="ClirMmi" msgid="7784673673446833091">"លេខសម្គាល់អ្នកហៅចេញ"</string>
<string name="CfMmi" msgid="5123218989141573515">"បញ្ជូនការហៅបន្ត"</string>
<string name="CwMmi" msgid="9129678056795016867">"រង់ចាំការហៅ"</string>
@@ -125,7 +125,7 @@
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិនបានបញ្ជូនបន្ត"</string>
<string name="fcComplete" msgid="3118848230966886575">"កូដលក្ខណៈពេញលេញ។"</string>
<string name="fcError" msgid="3327560126588500777">"បញ្ហាការតភ្ជាប់ ឬកូដលក្ខណៈមិនត្រឹមត្រូវ។"</string>
- <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string>
+ <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string>
<string name="httpError" msgid="7956392511146698522">"មានកំហុសបណ្ដាញ។"</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"រកមិនឃើញ URL ។"</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍ផ្ទៀងផ្ទាត់តំបន់បណ្ដាញមិនត្រូវបានគាំទ្រ។"</string>
@@ -183,7 +183,7 @@
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើកសំឡេង"</string>
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេលជិះយន្តហោះ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បានបើករបៀបពេលជិះយន្តហោះ"</string>
- <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string>
+ <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ការកំណត់"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"របៀបសុវត្ថិភាព"</string>
@@ -195,7 +195,7 @@
<string name="permgrouplab_messages" msgid="7521249148445456662">"សាររបស់អ្នក"</string>
<string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និងសរសេរសារ SMS, អ៊ីមែល និងសារផ្សេងៗទៀតរបស់អ្នក។"</string>
<string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នក"</string>
- <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string>
+ <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string>
<string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មានសង្គមរបស់អ្នក"</string>
<string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីទំនាក់ទំនង និងការភ្ជាប់សង្គមរបស់អ្នក។"</string>
<string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំងរបស់អ្នក"</string>
@@ -384,7 +384,7 @@
<string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យកម្មវិធីមើលគ្រាប់ចុចដែលអ្នកចុចពេលមានអន្តរកម្មជាមួយកម្មវិធីផ្សេង (ដូចជា បញ្ចូលពាក្យសម្ងាត់)។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចងទៅវិធីសាស្ត្របញ្ចូល"</string>
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃវិធីសាស្ត្របញ្ចូល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string>
+ <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មភាពងាយស្រួល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindPrintService" msgid="8462815179572748761">"ចងសេវាកម្មបោះពុម្ព"</string>
<string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
@@ -404,7 +404,7 @@
<string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"អនុញ្ញាតឲ្យម្ចាស់គ្រប់គ្រងឃ្លាសម្រាប់ការរកឃើញពាក្យជាសំឡេង។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់ទៅការបង្ហាញពីចម្ងាយ"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលនៃការបង្ហាញពីចម្ងាយ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string>
+ <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់ទៅសេវាកម្មក្រុមហ៊ុនផ្ដល់ច្រក"</string>
<string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅក្រុមហ៊ុនផ្ដល់ច្រកដែលបានចុះឈ្មោះ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
@@ -412,7 +412,7 @@
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យម្ចាស់ផ្ញើគោលបំណងទៅអ្នកគ្រប់គ្រងឧបករណ៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់ទៅការបញ្ចូលទូរទស្សន៍"</string>
<string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតខ្ពស់នៃការបញ្ចូលទូរទស្សន៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string>
+ <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string>
<string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាតឲ្យម្ចាស់បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍សកម្មចេញ។ មិនគួរប្រើសម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរទិសអេក្រង់"</string>
<string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យកម្មវិធីប្ដូរការបង្វិលអេក្រង់នៅពេលណាមួយ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
@@ -424,9 +424,9 @@
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យកម្មវិធីស្នើសញ្ញាដែលបានផ្ដល់ត្រូវផ្ញើទៅដំណើរការស្ថិតស្ថេរទាំងអស់។"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើឲ្យកម្មវិធីដំណើរការជានិច្ច"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យកម្មវិធីធ្វើជាផ្នែកស្ថិតស្ថេរដោយខ្លួនឯងក្នុងអង្គចងចាំ។ វាអាចកំណត់អង្គចងចាំដែលអាចប្រើបានចំពោះកម្មវិធីផ្សេងៗ ដោយធ្វើឲ្យកុំព្យូទ័របន្ទះយឺត។"</string>
- <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string>
<string name="permlab_deletePackages" msgid="184385129537705938">"លុបកម្មវិធី"</string>
- <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។ "</string>
+ <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។"</string>
<string name="permlab_clearAppUserData" msgid="274109191845842756">"លុបទិន្នន័យរបស់កម្មវិធីផ្សេង"</string>
<string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យកម្មវិធីសម្អាតទិន្នន័យអ្នកប្រើ។"</string>
<string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុបឃ្លាំងសម្ងាត់កម្មវិធីផ្សេងៗ"</string>
@@ -477,7 +477,7 @@
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងកុំព្យូទ័របន្ទះ រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនងជាក់លាក់។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនងរបស់អ្នក។"</string>
<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងទូរស័ព្ទរបស់អ្នក រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬបានទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនាក់ជាក់លាក់។ សិទ្ធិនេះឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនង។"</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"អានកំណត់ហេតុហៅ"</string>
- <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
+ <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
<string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យកម្មវិធីអានបញ្ជីហៅទូរស័ព្ទរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរបញ្ជីហៅ"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យកម្មវិធីកែបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នករួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុប ឬកែបញ្ជីហៅរបស់អ្នក។"</string>
@@ -613,7 +613,7 @@
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យកម្មវិធីកំណត់ជំនួយទំហំផ្ទាំងរូបភាពប្រព័ន្ធ។"</string>
<string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់ប្រព័ន្ធទៅលំនាំដើមរោងចក្រឡើងវិញ"</string>
<string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យកម្មវិធីកំណត់ប្រព័ន្ធដូចការកំណត់ចេញពីរោងចក្រឡើងវិញពេញលេញ ដោយលុបទិន្នន័យ ការកំណត់រចនាសម្ព័ន្ធ និងកម្មវិធីបានដំឡើង។"</string>
- <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</string>
+ <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</string>
<string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យកម្មវិធីប្ដូរម៉ោងកុំព្យូទ័របន្ទះ។"</string>
<string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យកម្មវិធីប្ដូរម៉ោងទូរស័ព្ទ។"</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់តំបន់ពេលវេលា"</string>
@@ -779,7 +779,7 @@
<string-array name="organizationTypes">
<item msgid="7546335612189115615">"កន្លែងធ្វើការ"</item>
<item msgid="4378074129049520373">"ផ្សេងៗ"</item>
- <item msgid="3455047468583965104">"តាមតម្រូវការ"</item>
+ <item msgid="3455047468583965104">"តាមតម្រូវការ"</item>
</string-array>
<string-array name="imProtocols">
<item msgid="8595261363518459565">"AIM"</item>
@@ -795,7 +795,7 @@
<string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string>
<string name="phoneTypeMobile" msgid="6501463557754751037">"ចល័ត"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែងធ្វើការ"</string>
- <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string>
+ <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string>
<string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារផ្ទះ"</string>
<string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string>
<string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string>
@@ -920,7 +920,7 @@
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បីដោះសោ ចូលគណនី Google របស់អ្នក។"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះអ្នកប្រើ (អ៊ីមែល)"</string>
- <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
+ <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -965,7 +965,7 @@
<string name="factorytest_failed" msgid="5410270329114212041">"បានបរាជ័យក្នុងការសាកល្បងរោងចក្រ"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវបានគាំទ្រសម្រាប់តែកញ្ចប់បានដំឡើងក្នុង /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"រកមិនឃើញកញ្ចប់ដែលផ្ដល់សកម្មភាព FACTORY_TEST ។"</string>
- <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string>
+ <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រមានចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់ការរុករក"</string>
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"ឲ្យកម្មវិធីកំណត់សំឡេងរោទ៍ក្នុងកម្មវិធីនាឡិការោទ៍បានដំឡើង។ កម្មវិធីនាឡិការោទ៍មួយចំនួនអាចមិនអនុវត្តលក្ខណៈនេះ។"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"បន្ថែមសារជាសំឡេង"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ឲ្យកម្មវិធីបន្ថែមសារទៅប្រអប់ទទួលសារជាសំឡេងរបស់អ្នក។"</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"កែសិទ្ធិទីតាំងភូមិសាស្ត្ររបស់កម្មវិធីអ៊ីនធឺណិត"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ឲ្យកម្មវិធីកែសិទ្ធិទីតាំងភូមិសាស្ត្ររបស់កម្មវិធីអ៊ីនធឺណិត។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីឲ្យផ្ញើព័ត៌មានទីតាំងទៅតំបន់បណ្ដាញដោយបំពាន។"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ផ្ទៀងផ្ទាត់កញ្ចប់"</string>
@@ -1023,7 +1027,7 @@
<string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string>
<string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string>
<string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
- <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
+ <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
<string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string>
<string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string>
<string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរកសំណួរ"</string>
@@ -1107,18 +1111,18 @@
<string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="preposition_for_time" msgid="5506831244263083793">"នៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="preposition_for_year" msgid="5040395640711867177">"ក្នុងឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string>
- <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
+ <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
<string name="days" msgid="4774547661021344602">"ថ្ងៃ"</string>
<string name="hour" msgid="2126771916426189481">"ម៉ោង"</string>
<string name="hours" msgid="894424005266852993">"ម៉ោង"</string>
- <string name="minute" msgid="9148878657703769868">"នាទី"</string>
+ <string name="minute" msgid="9148878657703769868">"នាទី"</string>
<string name="minutes" msgid="5646001005827034509">"នាទី"</string>
- <string name="second" msgid="3184235808021478">"វិនាទី"</string>
+ <string name="second" msgid="3184235808021478">"វិនាទី"</string>
<string name="seconds" msgid="3161515347216589235">"វិនាទី"</string>
- <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
- <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
- <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
- <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
+ <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
+ <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
+ <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
+ <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
<plurals name="duration_seconds">
<item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item>
<item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item>
@@ -1134,12 +1138,12 @@
<string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហាវីដេអូ"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូនេះមិនត្រឹមត្រូវសម្រាប់ចរន្តចូលឧបករណ៍នេះ។"</string>
<string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិនអាចចាក់វីដេអូនេះ។"</string>
- <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string>
+ <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string>
<string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="noon" msgid="7245353528818587908">"រសៀល"</string>
<string name="Noon" msgid="3342127745230013127">"រសៀល"</string>
<string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string>
- <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string>
+ <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="6876518925844129331">"ជ្រើសទាំងអស់"</string>
@@ -1156,13 +1160,13 @@
<string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្របញ្ចូល"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាពអត្ថបទ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់ទំហំផ្ទុក"</string>
- <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
+ <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងដំណើរការ"</string>
<string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ ដើម្បីមើលព័ត៌មានបន្ថែម ឬបញ្ឈប់កម្មវិធី។"</string>
- <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string>
- <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
- <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string>
- <string name="no" msgid="5141531044935541497">"បោះបង់"</string>
+ <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string>
+ <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
+ <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string>
+ <string name="no" msgid="5141531044935541497">"បោះបង់"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string>
<string name="loading" msgid="7933681260296021180">"កំពុងផ្ទុក..."</string>
<string name="capital_on" msgid="1544682755514494298">"បើក"</string>
@@ -1171,7 +1175,7 @@
<string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើសកម្មវិធីដើម"</string>
<string name="alwaysUse" msgid="4583018368000610438">"ប្រើតាមលំនាំដើមសម្រាប់សកម្មភាពនេះ។"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាតលំនាំដើមក្នុងការកំណត់ប្រព័ន្ធ > កម្មវិធី > ទាញយក។"</string>
- <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើសសកម្មភាព"</string>
+ <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើសសកម្មភាព"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើសកម្មវិធីសម្រាប់ឧបករណ៍យូអេសប៊ី"</string>
<string name="noApplications" msgid="2991814273936504689">"គ្មានកម្មវិធីអាចអនុវត្តសកម្មភាពនេះ។"</string>
<string name="aerr_title" msgid="1905800560317137752"></string>
@@ -1182,7 +1186,7 @@
<string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string>
<string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិនឆ្លើយតប។ តើអ្នកចង់បិទវា?"</string>
<string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិនឆ្លើយតប។ \n\nតើអ្នកចង់បិទវាឬ?"</string>
- <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string>
+ <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string>
<string name="report" msgid="4060218260984795706">"រាយការណ៍"</string>
<string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រក្លាយជាមិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string>
@@ -1264,19 +1268,19 @@
<string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះអាចកាត់លុយ"</font>" លើគណនីចល័តរបស់អ្នក។"</string>
<string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វានឹងគិតថ្លៃសេវាកម្មលើគណនីចល័តរបស់អ្នក។"</font></string>
<string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string>
- <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string>
+ <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំជម្រើសរបស់ខ្ញុំ"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នកអាចប្ដូរវាពេលក្រោយក្នុងការកំណត់ > កម្មវិធី"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាតជានិច្ច"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំអនុញ្ញាត"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"បានដកស៊ីមកាតចេញ"</string>
- <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string>
+ <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string>
<string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string>
<string name="sim_added_title" msgid="3719670512889674693">"បានបន្ថែមស៊ីមកាត"</string>
<string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ ដើម្បីចូលដំណើរការបណ្ដាញចល័ត។"</string>
<string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើមឡើងវិញ"</string>
- <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string>
- <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string>
+ <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string>
+ <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string>
<string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string>
<string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string>
<string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string>
@@ -1354,7 +1358,7 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យកម្មវិធីដកសេវាកម្មនៃកម្មវិធីផ្ទុកលំនាំដើម ដើម្បីចម្លងមាតិកា។ មិនសម្រាប់ប្រើដោយកម្មវិធីលំនាំដើម។"</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"នាំផ្លូវលទ្ធផលមេឌៀ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យកម្មវិធីនាំផ្លូវលទ្ធផលមេឌៀទៅឧបករណ៍ខាងក្រៅផ្សេង។"</string>
- <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string>
<string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យកម្មវិធីចូលការផ្ទុកមានសុវត្ថិភាព keguard ។"</string>
<string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យការបង្ហាញ និងលាក់ការការពារ"</string>
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យកម្មវិធីគ្រប់គ្រង keguard ។"</string>
@@ -1373,7 +1377,7 @@
<string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
<string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string>
<string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string>
- <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
+ <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
<string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string>
<string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string>
<string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string>
@@ -1382,7 +1386,7 @@
<string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធីមួយ ឬច្រើនដូចខាងក្រោមស្នើសិទ្ធិ ដើម្បីចូលគណនីរបស់អ្នកឥឡូវ និងពេលអនាគត។"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"តើអ្នកចង់អនុញ្ញាតសំណើនេះ?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើចូល"</string>
- <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
+ <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
<string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"បានស្នើសិទ្ធិ"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បានស្នើសិទ្ធិ\nសម្រាប់គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string>
@@ -1405,12 +1409,12 @@
<string name="no_file_chosen" msgid="6363648562170759465">"គ្មានឯកសារបានជ្រើស"</string>
<string name="reset" msgid="2448168080964209908">"កំណត់ឡើងវិញ"</string>
<string name="submit" msgid="1602335572089911941">"ដាក់ស្នើ"</string>
- <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string>
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string>
<string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ ដើម្បីចេញពីរបៀបរថយន្ត។"</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬហតស្ពតសកម្ម"</string>
<string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ ដើម្បីរៀបចំ។"</string>
<string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string>
- <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
+ <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
<string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string>
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការប្រើទិន្នន័យចល័តខ្ពស់"</string>
<string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ ដើម្បីស្វែងយល់បន្ថែមអំពីការប្រើទិន្នន័យចល័ត។"</string>
@@ -1436,7 +1440,7 @@
<string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍ផ្ទុកយូអេសប៊ីបច្ចុប្បន្នកំពុងប្រើដោយកុំព្យូទ័រ។"</string>
<string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្នកាតអេសឌីកំពុងប្រើដោយកុំព្យូទ័រ"</string>
<string name="media_unknown_state" msgid="729192782197290385">"មិនស្គាល់ស្ថានភាពមេឌៀខាងក្រៅ។"</string>
- <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string>
+ <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string>
<string name="find" msgid="4808270900322985960">"រក"</string>
<string name="websearch" msgid="4337157977400211589">"ស្វែងរកតាមបណ្ដាញ"</string>
<string name="find_next" msgid="5742124618942193978">"រកបន្ទាប់"</string>
@@ -1452,7 +1456,7 @@
<string name="sync_undo_deletes" msgid="2941317360600338602">"មិនធ្វើការលុបវិញ"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"មិនធ្វើអ្វីទេឥឡូវ"</string>
<string name="choose_account_label" msgid="5655203089746423927">"ជ្រើសគណនី"</string>
- <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string>
+ <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែមគណនី"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string>
@@ -1471,15 +1475,15 @@
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើនឆ្នាំ"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយឆ្នាំ"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូររបៀប"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
- <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើសកម្មវិធី"</string>
+ <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើសកម្មវិធី"</string>
<string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិនអាចចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</string>
+ <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</string>
<string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែករំលែកជាមួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រងការរុញ។ ប៉ះ & សង្កត់។"</string>
<string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស ដើម្បីដោះសោ។"</string>
@@ -1493,7 +1497,7 @@
<string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍ផ្ទុកខាងក្នុង"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"កាតអេសឌី"</string>
<string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍ផ្ទុកយូអេសប៊ី"</string>
- <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
+ <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមានប្រើទិន្នន័យ"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បីមើលការប្រើ និងការកំណត់។"</string>
<string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បានបិទទិន្នន័យ 2G-3G"</string>
@@ -1550,7 +1554,7 @@
<string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"មិនទំនេរ"</string>
<string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុងប្រើ"</string>
- <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួតគ្នា"</string>
<string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
@@ -1582,7 +1586,7 @@
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string>
- <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -1691,7 +1695,7 @@
<string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
<string name="mediasize_unknown_portrait" msgid="3088043641616409762">"មិនស្គាល់បញ្ឈរ"</string>
<string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិនស្គាល់ទេសភាព"</string>
- <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string>
+ <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string>
<string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុសក្នុងការសរសេរមាតិកា"</string>
<string name="reason_unknown" msgid="6048913880184628119">"មិនស្គាល់"</string>
<string name="reason_service_unavailable" msgid="7824008732243903268">"មិនបានបើកសេវាកម្មបោះពុម្ព"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 43d1b25..cffe6bb 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"앱이 설치된 알람 시계 앱에서 알람을 설정할 수 있도록 허용합니다. 일부 알람 시계 앱에는 이 기능이 구현되지 않을 수 있습니다."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"음성사서함 추가"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"앱이 음성사서함에 메시지를 추가할 수 있도록 허용합니다."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"브라우저 위치 정보 권한 수정"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"앱이 브라우저의 위치 정보 권한을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 임의의 웹사이트에 위치 정보를 보낼 수 있습니다."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"패키지 확인"</string>
@@ -1362,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"애플리케이션이 Trust 상태에서의 변경사항을 수신할 수 있도록 허용합니다."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Trust Agent 제공"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"애플리케이션이 Trust Agent를 제공할 수 있도록 허용합니다."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Trust Agent 설정 메뉴를 실행합니다."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"애플리케이션에서 Trust Agent의 동작을 변경하는 활동을 실행하도록 허용합니다."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Trust Agent 서비스에 연결"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"애플리케이션이 Trust Agent 서비스에 바인딩할 수 있도록 허용합니다."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"업데이트 및 복구 시스템과 상호작용"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index e386a24..b44c9e0 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"ອະນຸຍາດໃຫ້ແອັບຯຕັ້ງໂມງປຸກໃນແອັບຯໂມງປຸກທີ່ຕິດຕັ້ງໄວ້. ບາງແອັບຯໂມງປຸກອາດບໍ່ມີຄຸນສົມບັດແບບນີ້ເທື່ອ."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ເພີ່ມຂໍ້ຄວາມສຽງ"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"ອະນຸຍາດໃຫ້ແອັບຯ ສາມາດເພີ່ມຂໍ້ຄວາມໃສ່ອິນບັອກຂໍ້ຄວາມສຽງຂອງທ່ານໄດ້."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"ອ່ານຂໍ້ຄວາມສຽງທັງໝົດ"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"ອະນຸຍາດໃຫ້ແອັບຯອ່ານຂໍ້ຄວາມສຽງທັງໝົດຂອງທ່ານ."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ແກ້ໄຂສິດທາງສະຖານທີ່ພູມສາດຂອງໂປຣແກຣມທ່ອງເວັບ"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂ ການອະນຸຍາດຕຳແໜ່ງທາງພູມສາດ ຂອງໂປຣແກຣມທ່ອງເວັບ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດໃຊ້ຄຸນສົມບັດນີ້ ເພື່ອສົ່ງຂໍ້ມູນສະຖານທີ່ໄປໃຫ້ເວັບໄຊຕ່າງໆໄດ້."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ຢັ້ງຢືນແພັກເກດ"</string>
@@ -1703,7 +1705,7 @@
<string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະຈຸບັນ"</string>
<string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string>
<string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string>
- <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string>
+ <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string>
<string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ."</string>
<string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ສັ້ນເກີນໄປ. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ."</string>
<plurals name="restr_pin_countdown">
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index be7e571..82548a0 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Leidžiama programai nustatyti signalą įdiegtoje žadintuvo programoje. Kai kuriose žadintuvo programose ši funkcija gali nebūti nevykdoma."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"pridėti balso pašto pranešimų"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Leidžia programai pridėti pranešimų prie jūsų balso pašto gautųjų."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"skaityti visus balso pašto pranešimus"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Programai leidžiama skaityti visus balso pašto pranešimus."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"keisti naršyklės geografinės vietos leidimus"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Leidžiama programai keisti naršyklės geografinės vietos leidimus. Kenkėjiškos programos gali tai naudoti, kad leistų siųsti vietos informaciją abejotinoms svetainėms."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"patikrinti paketus"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index b421da4..798a39d 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Ļauj lietotnei iestatīt signālu instalētajā modinātājpulksteņa lietotnē. Dažās modinātājpulksteņu lietotnēs šo funkciju, iespējams, nevar ieviest."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"pievienot balss pastu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ļauj lietotnei pievienot ziņojumus jūsu balss pasta iesūtnei."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"pārveidot pārlūkprogrammas ģeogrāfiskās atrašanās vietas atļaujas"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ļauj lietotnei modificēt pārlūkprogrammas ģeogrāfiskās atrašanās vietas atļaujas. Ļaunprātīgas lietotnes to var izmantot, lai atļautu atrašanās vietas informācijas sūtīšanu uz citām vietnēm."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakotņu verificēšana"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 803128f..d19d607 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Апп нь суулгагдсан сэрүүлэгний апп дээр сэрүүлэг тохируулах боломжтой. Зарим сэрүүлэгний апп нь энэ функцийг дэмжихгүй байж болзошгүй."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"дуут шуудан нэмэх"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Таны дуут шуудангийн ирсэн мэйлд зурвас нэмэхийг апп-д зөвшөөрөх."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"бүх дуут шууданг унших"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Апп-д таны бүх дуут шууданг унших боломж олгоно."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Хөтчийн геобайршлын зөвшөөрлийг өөрчлөх"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын веб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"багцийг тулгах"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 1d1a75b..49347a7 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Mod selamat"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Apl peribadi"</string>
- <!-- no translation found for managed_profile_label (4287077106125758391) -->
- <skip />
+ <string name="managed_profile_label" msgid="4287077106125758391">"Kerja Android"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Perkhidmatan yang anda perlu bayar"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Melakukan perkara yang boleh mengenakan bayaran kepada anda."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Mesej anda"</string>
@@ -402,7 +401,7 @@
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"terikat kepada interaksi suara"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan interaksi suara. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"urus frasa kunci suara"</string>
- <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Membenarkan pemegang menguruskan frasa kunci untuk pengesahan sebutan laluan. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+ <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Membenarkan pemegang mengurus frasa kunci untuk pengesahan sebutan laluan suara. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"terikat kepada paparan jauh"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi paparan jauh. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"terikat kepada perkhidmatan widget"</string>
@@ -1002,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Membenarkan apl untuk menetapkan penggera dalam apl penggera jam yang dipasang. Sesetengah applikasi jam penggera tidak boleh melaksanakan ciri ini."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"tambah mel suara"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Membenarkan apl untuk menambahkan mesej pada peti masuk mel suara anda."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ubah suai kebenaran geolokasi Penyemak Imbas"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Membenarkan apl untuk mengubah suai kebenaran geolokasi Penyemak Imbas. Apl hasad boleh menggunakannya untuk membenarkan menghantar maklumat lokasi kepada laman web sembarangan."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"sahkan pakej"</string>
@@ -1363,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Membenarkan aplikasi mendengar perubahan dalam keadaan amanah."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Sediakan ejen amanah."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Membenarkan aplikasi menyediakan ejen amanah."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lancarkan menu tetapan ejen amanah."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Membenarkan aplikasi melancarkan aktiviti yang mengubah tingkah laku ejen amanah."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Mengikat kepada perkhidmatan ejen amanah"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Membenarkan aplikasi terikat kepada perkhidmatan ejen amanah."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Berinteraksi dengan kemas kini dan sistem pemulihan"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 31fa11d..40764c7 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -401,7 +401,7 @@
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"binde seg til en tjeneste for talehandlinger"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Gir innehaveren tillatelse til å binde til toppnivået av brukergrensesnittet for en tjeneste for talehandlinger. Dette skal ikke være nødvendig for vanlige apper."</string>
<string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"administrer nøkkelfraser for stemmebruk"</string>
- <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Tillater innehaveren å administrere nøkkelfraser for gjenkjennelse av talekommandoord. Skal aldri være nødvendig for normale apper."</string>
+ <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Tillater eieren å administrere nøkkelfraser for gjenkjennelse av talekommandoord. Skal aldri være nødvendig for normale apper."</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"binde til ekstern skjerm"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Lar innehaveren binde seg til det øverste grensesnittnivået for ekstern skjerm. Skal aldri være nødvendig for vanlige apper."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"binde til modultjenste"</string>
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Lar appen stille inn alarmen for en installert alarmklokke-app. Enkelte alarmklokke-apper implementerer kanskje ikke denne funksjonen."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"legge til talepost"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Lar appen legge til meldinger i talepostkassen din."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"endre nettleserens tillatelser for geoposisjonering"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Lar appen endre nettleserens tillatelser for geoposisjonering. Ondsinnede apper kan bruke dette for å tillate sending av posisjonsinformasjon til vilkårlige nettsteder."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"bekrefte pakker"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a655f36c..37c2594 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Hiermee kan de app een alarm instellen in een geïnstalleerde wekkerapp. Deze functie wordt door sommige wekkerapps niet geïmplementeerd."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"voicemail toevoegen"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Hiermee kan de app berichten toevoegen aan de inbox van uw voicemail."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"alle voicemails lezen"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Hiermee kan de app al uw voicemails lezen."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"geolocatierechten voor browser aanpassen"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Hiermee kan de app de geolocatierechten van de browser aanpassen. Schadelijke apps kunnen dit gebruiken om locatiegegevens te verzenden naar willekeurige websites."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakketten controleren"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c30ad74..a3282e9 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Pozwala aplikacji na ustawienie alarmu w zainstalowanej aplikacji budzika. Funkcja ta może nie być zaimplementowana w niektórych aplikacjach tego typu."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"dodawanie poczty głosowej"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Pozwala aplikacji na dodawanie wiadomości do skrzynki odbiorczej poczty głosowej."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"odczyt całej poczty głosowej"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Zezwala aplikacji na odczyt całej Twojej poczty głosowej."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modyfikowanie pozwoleń przeglądarki dotyczących lokalizacji geograficznej"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Pozwala aplikacji na modyfikowanie uprawnień przeglądarki dotyczących lokalizacji geograficznej. Złośliwe aplikacje mogą używać tej opcji do wysyłania informacji o lokalizacji do dowolnych witryn."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"weryfikowanie pakietów"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 2e4e423..e85d902 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que a aplicação defina um alarme numa aplicação de despertador instalada. Algumas aplicações de despertador podem não integrar esta funcionalidade."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adicionar correio de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que a aplicação adicione mensagens à sua caixa de entrada de correio de voz."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"ler todo o correio de voz"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Permite que a aplicação leia todo o correio de voz."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificar permissões de geolocalização do Navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que a aplicação modifique as permissões de geolocalização do navegador. As aplicações maliciosas podem usar isto para permitir o envio de informações de localização para Web sites arbitrárias."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar pacotes"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que uma aplicação registe alterações no trust state."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fornecer um agente fidedigno."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que uma aplicação forneça um agente fidedigno."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Inicie o menu de definições do agente de fidedignidade."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permite que uma aplicação inicie uma atividade que altere o comportamento do agente de fidedignidade."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Vincular a um serviço de trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que uma aplicação fique vinculada a um serviço de trust agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir com o sistema de recuperação e de atualização"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 3df7ecb..59328f5 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que o aplicativo defina um alarme em um aplicativo despertador instalado. Alguns aplicativos despertador podem não implementar este recurso."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adicionar correio de voz"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que o aplicativo adicione mensagens a sua caixa de entrada do correio de voz."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modifique as permissões de geolocalização de seu navegador"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que o aplicativo modifique as permissões de geolocalização do navegador. Aplicativos maliciosos podem usar isso para permitir o envio de informações locais para sites arbitrários."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar pacotes"</string>
@@ -1362,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que o aplicativo detecte alterações no estado de confiança."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fornecer um agente de confiança."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que um aplicativo forneça um agente de confiança."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Abra o menu de configurações do agente de confiança."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permite que um aplicativo inicie uma atividade que altera o comportamento do agente de confiança."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Associar a um serviço de agente de confiança"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que o aplicativo se associe a um serviço de agente de confiança."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir com o sistema de atualizações e recuperação"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index ff26d8f..83ba3a6 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1666,6 +1666,10 @@
<skip />
<!-- no translation found for permdesc_addVoicemail (6604508651428252437) -->
<skip />
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<!-- no translation found for permlab_writeGeolocationPermissions (5962224158955273932) -->
<skip />
<!-- no translation found for permdesc_writeGeolocationPermissions (1083743234522638747) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 110b243..11236b5 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Mod sigur"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistemul Android"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Aplicații personale"</string>
- <!-- no translation found for managed_profile_label (4287077106125758391) -->
- <skip />
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android pentru serviciu"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Servicii cu plată"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Efectuează acţiuni care sunt cu plată."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Mesajele dvs."</string>
@@ -1002,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permite aplicaţiei să seteze o alarmă într-o aplicaţie de ceas cu alarmă instalată. Este posibil ca unele aplicaţii de ceas cu alarmă să nu implementeze această funcţie."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"adăugare mesagerie vocală"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite aplicaţiei să adauge mesaje în Mesaje primite în mesageria vocală."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificare permisiuni pentru locaţia geografică a browserului"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite aplicaţiei să modifice permisiunile privind locaţia geografică a browserului. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a permite trimiterea informaţiilor privind locaţia către site-uri web arbitrare."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificare pachete"</string>
@@ -1363,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite unei aplicații să detecteze modificările în starea de încredere."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Indicați un agent de încredere."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite unei aplicații să indice un agent de încredere."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lansați meniul de setări pentru agentul de încredere."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permite unei aplicații să lanseze o activitate care schimbă comportamentul agentului de încredere."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Asocierea la un serviciu „agenți de încredere”."</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite unei aplicații să se asocieze la un serviciu „agent de încredere”."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interacțiune cu sistemul de recuperare și de actualizare"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 12d1895..c5f4c13 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Приложение сможет настраивать будильник. Функция поддерживается не во всех программах."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"Добавление голосовых сообщений"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Приложение сможет добавлять голосовые сообщения в папку \"Входящие\"."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"Доступ к голосовой почте"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Чтение голосовых сообщений."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Изменение прав доступа к геоданным в браузере"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Приложение сможет изменять настройки доступа к геоданным в браузере. Вредоносные программы смогут таким образом отправлять информацию о местоположении на любые веб-сайты."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Проверка пакетов"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index ab596c5..eb685ba 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Umožňuje aplikácii nastaviť budík v nainštalovanej aplikácii budík. Niektoré aplikácie budíka nemusia túto funkciu implementovať."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"pridať hlasovú schránku"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Umožní aplikácii pridávať správy do doručenej pošty hlasovej schránky."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"zmeniť povolenia prehliadača poskytovať informácie o zemepisnej polohe"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Umožňuje aplikácii zmeniť povolenia prehliadača na poskytovanie údajov o zemepisnej polohe. Škodlivé aplikácie to môžu použiť na odosielanie informácií o polohe ľubovoľným webovým stránkam."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"overiť balíky"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index cc06fd3..47568d6 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Programu omogoča nastavitev alarma v nameščenem programu budilke. Nekateri programi budilke morda nimajo te funkcije."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"dodajanje odzivnika"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Programu omogoča dodajanje sporočil prejetim sporočilom odzivnika."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Spreminjanje dovoljenj za geolokacijo brskalnika"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Programu omogoča spreminjanje geolokacijskih dovoljenj v brskalniku. Zlonamerni programi lahko to izkoristijo za pošiljanje podatkov o lokaciji poljubnim spletnim mestom."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"preveri pakete"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 099e6ef..4758c94 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -189,7 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"Безбедни режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android систем"</string>
<string name="user_owner_label" msgid="6465364741001216388">"Личне апликације"</string>
- <string name="managed_profile_label" msgid="4287077106125758391">"Android за посао"</string>
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Услуге које се плаћају"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Покреће радње које могу да се плаћају."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Поруке"</string>
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Дозвољава апликацији да подеси аларм у инсталираној апликацији будилника. Неке апликације будилника можда не примењују ову функцију."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"додавање говорне поште"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Дозвољава апликацији да додаје поруке у пријемно сандуче говорне поште."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"читај сву говорну пошту"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Дозвољава апликацији да чита говорну пошту."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"измена дозвола за географске локације Прегледача"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Дозвољава апликацији да измени дозволе Прегледача за утврђивање географске локације. Злонамерне апликације то могу да злоупотребе и искористе за слање информација о локацији насумичним веб сајтовима."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"верификовање пакета"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Дозвољава апликацији да прати промене Trust стања."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Обезбеђивање поузданог агента."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Дозвољава апликацији да обезбеди поузданог агента."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Покрени мени подешавања поузданог агента."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Дозвољава апликацији да покреће активност која мења понашање поузданог агента."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Везивање за услугу Trust agents"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Дозвољава апликацији да се веже за услугу Trust agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Интеракција са системом за ажурирање и опоравак"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 46bb563..af85340 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"lägg till röstbrevlåda"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Gör att appen lägger till meddelanden i röstbrevlådans inkorg."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"läsa alla röstmeddelanden"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Tillåter att appen läser alla dina röstmeddelanden."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Ändra geografisk plats för webbläsaren"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillåter att appen ändrar webbläsarens behörigheter för geografisk plats. Skadliga appar kan använda detta för att tillåta att platsinformation skickas till godtyckliga webbplatser."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"kontrollera paket"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Tillåter att en app lyssnar efter ändringar i den betrodda agentens status."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Tillhandahåll en betrodd agent."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Tillåter att en app tillhandahåller en betrodd agent."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Öppna inställningsmenyn för betrodda agenter."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Tillåter att en app startar en aktivitet som ändrar den betrodda agentens beteende."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bind till en tjänst från en betrodd agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Tillåter att en app binds vid en tjänst från en betrodd agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagera med uppdaterings- och återställningssystemet"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 595c807..755b9d2 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Inaruhusu programu kuweka kengele katika programu iliyosakinishwa ya kengele. Programu zingine za kengele zinawezakosa kutekeleza kipengee hiki."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"ongeza barua ya sauti"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Huruhusu programu kuongeza mawasiliano kwenye kikasha cha ujumbe wa sauti."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"soma ujumbe wote wa sauti"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Huruhusu programu isome ujumbe wako wote wa sauti."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Rekebisha vibali vya Kivinjari cha eneo la jio"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Inaruhusu programu kurekebisha ruhusa za eneo la jio za kivinjari. Programu hasidi zinaweza tumia hii kuruhusu kutuma taarifa ya eneo kwa wavuti holela."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"thibitisha furushi"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Huruhusu programu kusikiliza mabadiliko katika hali ya kuaminiwa."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Toa wakala wa uaminifu."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Huruhusu programu kutoa wakala wa uaminifu."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Fungua menyu ya mipangilio ya madalali wa kuaminiwa."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Huruhusu programu kufungua kitendo ambacho hubadilisha tabia ya madalali wa kuaminiwa."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Funga kwenye huduma ya dalali wa kuaminiwa"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Huruhusu programu kufungamanisha kwenye huduma ya dalali wa kuaminiwa."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Ingiliana na sasisho na mfumo wa kurejesha"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index feabf4f..b03221d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -401,7 +401,7 @@
<string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"เชื่อมโยงกับโปรแกรมโต้ตอบด้วยเสียง"</string>
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"อนุญาตให้ผู้ใช้อุปกรณ์เชื่อมโยงกับอินเทอร์เฟซระดับบนสุดของบริการโต้ตอบด้วยเสียง ไม่จำเป็นสำหรับแอปทั่วไป"</string>
<string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"จัดการเสียงพูดวลีคำหลัก"</string>
- <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"ให้ผู้ใช้อุปกรณ์สามารถจัดการวลีคำหลักสำหรับการตรวจหาเสียงพูดคำที่นิยม ไม่จำเป็นสำหรับแอปทั่วไป"</string>
+ <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"ให้แอปสามารถจัดการวลีคำหลักสำหรับการตรวจหาเสียงพูดคำที่นิยม ไม่จำเป็นสำหรับแอปทั่วไป"</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ผูกกับจอแสดงผลระยะไกล"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"อนุญาตให้ผู้ใช้ผูกกับอินเทอร์เฟซระดับสูงสุดของจอแสดงผลระยะไกล ซึ่งแอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"เชื่อมโยงกับบริการวิดเจ็ต"</string>
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"อนุญาตให้แอปพลิเคชันตั้งเวลาปลุกในแอปพลิเคชันนาฬิกาปลุกที่ติดตั้ง แอปพลิเคชันนาฬิกาปลุกบางรายการอาจไม่ใช้คุณลักษณะนี้"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"เพิ่มข้อวามเสียง"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"อนุญาตให้แอปพลิเคชันเพิ่มข้อความลงในกล่องข้อความเสียงของคุณ"</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"อ่านข้อความเสียงทั้งหมด"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"อนุญาตให้แอปอ่านข้อความเสียงทั้งหมดของคุณ"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"แก้ไขการอนุญาตเกี่ยวกับการระบุตำแหน่งทางภูมิศาสตร์ของเบราว์เซอร์"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"อนุญาตให้แอปพลิเคชันแก้ไขการอนุญาตตำแหน่งทางภูมิศาสตร์ของเบราว์เซอร์ แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ในการส่งข้อมูลตำแหน่งไปยังเว็บไซต์ต่างๆ ได้ตามต้องการ"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ยืนยันแพ็กเกจ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 13b629e..af88c3c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Pinapayagan ang app na magtakda ng alarm sa isang naka-install na app ng alarm clock. Maaaring hindi ipatupad ng ilang apps ng alarm clock ang tampok na ito."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"magdagdag ng voicemail"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Pinapayagan ang app na magdagdag ng mga mensahe sa iyong inbox ng voicemail."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"baguhin ang mga pahintulot ng geolocation ng Browser"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Pinapayagan ang app na baguhin ang mga pahintulot sa geolocation ng Browser. Maaari itong gamitin ng nakakahamak na apps upang payagan ang pagpapadala ng impormasyon ng lokasyon sa mga hindi tukoy na web site."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"i-verify ang mga package"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 0c19b03..ee8c464 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Uygulamaya, çalar saat uygulamasının alarmını ayarlama izni verir. Bazı çalar saat uygulamaları bu özelliği uygulayamayabilir."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"sesli mesaj ekle"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Uygulamaya, sesli mesaj gelen kutunuza mesaj ekleme izni verir."</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Tarayıcı\'nın coğrafi konum izinlerini değiştir"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Uygulamaya, Tarayıcı\'nın coğrafi konum izinlerini değiştirme izni verir. Kötü amaçlı uygulamalar keyfi web sitelerine konum bilgisi gönderilmesini sağlamak için bunu kullanabilirler."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"paketleri doğrula"</string>
@@ -1362,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Bir uygulamanın, güven durumundaki değişiklikleri dinlemesine izin verir."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Güven aracısı sağlama."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Bir uygulamanın güven aracısı sağlamasına izin verir."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Güven aracısı ayarlar menüsünü başlat."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Bir uygulamanın güven aracısının çalışma biçimini değiştiren bir etkinlik başlatmasına izin verir."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Güven aracı hizmetine bağlan"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Bir uygulamanın, güven aracı hizmetine bağlanmasına izin verir."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Güncelleme ve kurtarma sistemiyle etkileşim kur"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 0d56975b..b85fc99 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Дозволяє програмі налаштовувати сигнал у встановленій програмі будильника. У деяких програмах будильника ця функція може не застосовуватися."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"додавати голосову пошту"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Дозволяє програмі додавати повідомлення в папку \"Вхідні\" голосової пошти."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"читати всі голосові повідомлення"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Додаток може читати всі голосові повідомлення."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"змінювати дозволи географічного місцезнаходження у веб-переглядачі"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Дозволяє програмі змінювати дозволи географічного місцезнаходження у веб-переглядачі. Шкідливі програми можуть використовувати це, щоб дозволяти надсилати інформацію про місцезнаходження довільним веб-сайтам."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"перевіряти пакети"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index dd13b45..b38fb87 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Cho phép ứng dụng đặt báo thức trong ứng dụng đồng hồ báo thức được cài đặt. Một số ứng dụng đồng hồ báo thức có thể không thực thi tính năng này."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"thêm thư thoại"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Cho phép ứng dụng thêm thông báo vào hộp thư thoại đến của bạn."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"đọc tất cả các thư thoại"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Cho phép ứng dụng này đọc tất cả các thư thoại của bạn."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"sửa đổi các quyền về vị trí địa lý của Trình duyệt"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Cho phép ứng dụng sửa đổi cấp phép vị trí địa lý của Trình duyệt. Ứng dụng độc hại có thể lợi dụng quyền này để cho phép gửi thông tin vị trí tới các trang web tùy ý."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"xác minh gói"</string>
@@ -1362,10 +1364,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Cho phép ứng dụng quan sát các thay đổi ở trạng thái đáng tin cậy."</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Cung cấp tác nhân đáng tin cậy."</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Cho phép ứng dụng cung cấp tác nhân đáng tin cậy."</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Khởi chạy trình đơn cài đặt đại lý đáng tin cậy."</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Cho phép ứng dụng khởi chạy hoạt động thay đổi hoạt động của đại lý đáng tin cậy."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Liên kết với một dịch vụ của đại lý đáng tin cậy"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Cho phép ứng dụng liên kết với một dịch vụ của đại lý đáng tin cậy."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Tương tác với hệ thống khôi phục và bản cập nhật"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 3a07366..d730c49 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -189,8 +189,7 @@
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
<string name="user_owner_label" msgid="6465364741001216388">"个人应用"</string>
- <!-- no translation found for managed_profile_label (4287077106125758391) -->
- <skip />
+ <string name="managed_profile_label" msgid="4287077106125758391">"Android Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"需要您付费的服务"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"执行可能需要您付费的操作。"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"您的信息"</string>
@@ -1002,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"允许应用在已安装的闹钟应用中设置闹钟。有些闹钟应用可能无法实现此功能。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"添加语音邮件"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"允许应用向您的语音信箱收件箱添加邮件。"</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改“浏览器”地理位置的权限"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允许应用修改“浏览器”的地理位置权限。恶意应用可能借此向任意网站发送位置信息。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"验证软件包"</string>
@@ -1363,10 +1366,8 @@
<string name="permdesc_trust_listener" msgid="8233895334214716864">"允许应用检测信任状态的变化。"</string>
<string name="permlab_provide_trust_agent" msgid="5465587586091358316">"提供信任的代理。"</string>
<string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"允许应用提供信任的代理。"</string>
- <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
- <skip />
- <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
- <skip />
+ <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"启动信任的代理设置菜单。"</string>
+ <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"允许应用启动可变更信任的代理行为的活动。"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"绑定至信任的代理服务"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"允许应用绑定至信任的代理服务。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"与更新和恢复系统互动"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 8e290b5..43a487b 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"允許應用程式在安裝的鬧鐘應用程式中設定鬧鐘,某些鬧鐘應用程式可能沒有這項功能。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"新增留言"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"允許應用程式將訊息加到您的留言信箱收件箱。"</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改瀏覽器地理資訊的權限"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允許應用程式修改瀏覽器的地理資訊權限。惡意應用程式可能會藉此允許將您的位置資訊任意傳送給某些網站。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"驗證套件"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6ab2b48..6658689 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -448,7 +448,7 @@
<string name="permlab_manageCaCertificates" msgid="1678391896786882014">"管理信任的憑證"</string>
<string name="permdesc_manageCaCertificates" msgid="4015644047196937014">"允許應用程式安裝 CA 憑證 (做為信任的憑證) 及解除安裝 CA 憑證。"</string>
<string name="permlab_bindJobService" msgid="3637568367978271086">"執行應用程式的預定背景作業"</string>
- <string name="permdesc_bindJobService" msgid="3473288460524119838">"這項權限可讓 Android 系統於收到要求時在背景執行應用程式。"</string>
+ <string name="permdesc_bindJobService" msgid="3473288460524119838">"這項權限可讓 Android 系統收到要求時在背景執行應用程式。"</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"讀寫 diag 擁有的資源"</string>
<string name="permdesc_diagnostic" msgid="6608295692002452283">"允許應用程式讀取或寫入診斷群組擁有的任何資源,例如 /dev 底下的檔案。這可能會影響系統的穩定性和安全性,因此應由製造商或電信業者操作,且只用在特定硬體診斷。"</string>
<string name="permlab_changeComponentState" msgid="6335576775711095931">"啟用或停用應用程式元件"</string>
@@ -1001,6 +1001,10 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"允許應用程式在安裝的鬧鐘應用程式中設定鬧鐘,某些鬧鐘應用程式可能無法執行這項功能。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"新增語音留言"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"允許應用程式將訊息新增至您的語音信箱收件匣。"</string>
+ <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+ <skip />
+ <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+ <skip />
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改瀏覽器地理資訊的權限"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允許應用程式修改瀏覽器的地理位置權限。請注意,惡意應用程式可能利用此功能允許將您的位置資訊任意傳送給某些網站。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"驗證套件"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 07238c3..820ea50 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1001,6 +1001,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Ivumela uhlelo lokusebenza ukuthi isethe i-alamu ensizeni efkiwe ye-alamu. Ezinye izinhlelo zokusebenza ze-alamu kungenzeka zingakusebenzisi lokho."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"engeza imeyili yezwi"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ivumela uhlelo lokusebenza ukwengeza imiyalezo kwibhokisi lakho lemeyili yezwi."</string>
+ <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"funda wonke amavoyisimeyili"</string>
+ <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Ivumela uhlelo lokusebenza ukuthi lufunde wonke amavoyisimeyili wakho."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Gugula izimvume zendawo Yesiphequluli"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ivumela uhlelo lokusebenza ukuthi iguqule izimvume eziphathelene nezindawo Zesiphequluli. Izuhlelo lokusebenza eziyingozi zingasebenzisa lokhu ukuvumela ukuvumela imininingwane yendawo kunoma imaphi amawebusayithi."</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"qinisekisa amaphakheji"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8d8f69e..d3e4790 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5250,10 +5250,18 @@
</declare-styleable>
<declare-styleable name="PathInterpolator">
+ <!-- The x coordinate of the first control point of the cubic Bezier -->
<attr name="controlX1" format="float" />
+ <!-- The y coordinate of the first control point of the cubic Bezier -->
<attr name="controlY1" format="float" />
+ <!-- The x coordinate of the second control point of the cubic Bezier -->
<attr name="controlX2" format="float" />
+ <!-- The y coordinate of the second control point of the cubic Bezier -->
<attr name="controlY2" format="float" />
+ <!-- The control points defined as a path.
+ When pathData is defined, then both of the control points of the
+ cubic Bezier will be ignored. -->
+ <attr name="pathData"/>
</declare-styleable>
<!-- ========================== -->
@@ -5403,6 +5411,12 @@
<declare-styleable name="PropertyAnimator">
<!-- Name of the property being animated. -->
<attr name="propertyName" format="string"/>
+ <!-- Name of the property being animated as the X coordinate of the pathData. -->
+ <attr name="propertyXName" format="string"/>
+ <!-- Name of the property being animated as the Y coordinate of the pathData. -->
+ <attr name="propertyYName" format="string"/>
+ <!-- The path used to animate the properties in the ObjectAnimator -->
+ <attr name="pathData"/>
</declare-styleable>
@@ -6733,6 +6747,8 @@
<attr name="switchPadding" format="dimension" />
<!-- Whether to split the track and leave a gap for the thumb drawable. -->
<attr name="splitTrack" />
+ <!-- Whether to draw on/off text. -->
+ <attr name="showText" format="boolean" />
</declare-styleable>
<declare-styleable name="Pointer">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 814d8fc..fc1d0df 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
@@ -873,17 +881,33 @@
<!-- The name of the logical parent of the activity as it appears in the manifest. -->
<attr name="parentActivityName" format="string" />
- <!-- Define an activity that will persist across reboots. If such an activity is in the
- Recents list when the device is shut off it will appear in the Recents list when
- the device is next powered on. To be persisted all activities in the task from the
- root activity up to the last activity before a <em>break</em> must be declared with
- the persistable attribute. A <em>break</em> is the first activity after the root
- started with Intent.FLAG_CLEAR_TASK_WHEN_RESET.
+ <!-- Define how an activity persist across reboots. Activities defined as "never" will not
+ be persisted. Those defined as "always" will be persisted. Those defined as "taskOnly"
+ will persist the root activity of the task only. See below for more detail as to
+ what gets persisted. -->
+ <attr name="persistableMode">
+ <!-- The default. If this activity forms the root of a task then that task will be
+ persisted across reboots but only the launching intent will be used. All
+ activities above this activity in the task will not be persisted. In addition
+ this activity will not be passed a PersistableBundle into which it could have
+ stored its state. -->
+ <enum name="persistRootOnly" value="0" />
+ <!-- If this activity forms the root of a task then that task will not be persisted
+ across reboots -->
+ <enum name="doNotPersist" value="1" />
+ <!-- If this activity forms the root of a task then the task and this activity will
+ be persisted across reboots. If the activity above this activity is also
+ tagged with the attribute <code>"persist"</code> then it will be persisted as well.
+ And so on up the task stack until either an activity without the
+ <code>persistableMode="persistAcrossReboots"</code> attribute or one that was launched
+ with the flag Intent.FLAG_CLEAR_TASK_WHEN_RESET is encountered.
- <p>Activities that are declared with the persistable attribute will be provided with a
- forced-persistable Bundle in onCreate() and onSavedInstanceState(), and must only
- be passed a persistable Bundle in their Intent.extras. -->
- <attr name="persistable" format="boolean" />
+ <p>Activities that are declared with the persistAcrossReboots attribute will be
+ provided with a PersistableBundle in onSavedInstanceState(), These activities may
+ use this PeristableBundle to save their state. Then, following a reboot, that
+ PersistableBundle will be provided back to the activity in its onCreate() method. -->
+ <enum name="persistAcrossReboots" value="2" />
+ </attr>
<!-- This attribute specifies that an activity shall become the root activity of a
new task each time it is launched. Using this attribute permits the user to
@@ -961,6 +985,14 @@
Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} -->
<attr name="autoRemoveFromRecents" format="boolean" />
+ <!-- Tasks whose root has this attribute set to true will replace baseIntent with that of the
+ next activity in the task. If the next activity also has this attribute set to true then
+ it will yield the baseIntent to any activity that it launches in the same task. This
+ continues until an activity is encountered which has this attribute set to false. False
+ is the default. This attribute set to true also permits activity's use of the
+ TaskDescription to change labels, colors and icons in the recent task list. -->
+ <attr name="relinquishTaskIdentity" format="boolean" />
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1044,6 +1076,7 @@
<attr name="testOnly" />
<attr name="backupAgent" />
<attr name="allowBackup" />
+ <attr name="fullBackupOnly" />
<attr name="killAfterRestore" />
<attr name="restoreNeedsApplication" />
<attr name="restoreAnyVersion" />
@@ -1623,11 +1656,12 @@
<!-- @hide This broacast receiver will only receive broadcasts for the
primary user. Can only be used with receivers. -->
<attr name="primaryUserOnly" format="boolean" />
- <attr name="persistable" />
+ <attr name="persistableMode" />
<attr name="allowEmbedded" />
<attr name="documentLaunchMode" />
<attr name="maxRecents" />
<attr name="autoRemoveFromRecents" />
+ <attr name="relinquishTaskIdentity" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a4f78bd..d350ef2 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>
@@ -1597,5 +1597,12 @@
<item>"1,sim,0,modem"</item>
</string-array>
+ <!-- This string array can be overriden to add an additional DRM support for WebView EME. -->
+ <!-- Array of "[keySystemName],[UuidOfMediaDrm]" -->
+ <string-array name="config_keySystemUuidMapping" translatable="false">
+ <!-- Example:
+ <item>"x-com.microsoft.playready,9A04F079-9840-4286-AB92-E65BE0885F95"</item>
+ -->
+ </string-array>
</resources>
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 1580d69..07a1e51 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2136,7 +2136,7 @@
<public type="attr" name="colorControlActivated" />
<public type="attr" name="colorButtonNormal" />
<public type="attr" name="colorControlHighlight" />
- <public type="attr" name="persistable" />
+ <public type="attr" name="persistableMode" />
<public type="attr" name="titleTextAppearance" />
<public type="attr" name="subtitleTextAppearance" />
<public type="attr" name="slideEdge" />
@@ -2206,6 +2206,10 @@
<public type="attr" name="buttonTintMode" />
<public type="attr" name="thumbTint" />
<public type="attr" name="thumbTintMode" />
+ <public type="attr" name="fullBackupOnly" />
+ <public type="attr" name="propertyXName" />
+ <public type="attr" name="propertyYName" />
+ <public type="attr" name="relinquishTaskIdentity" />
<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/styles_material.xml b/core/res/res/values/styles_material.xml
index 25307b9..75f905c 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -493,11 +493,12 @@
<item name="thumb">@drawable/switch_thumb_material_anim</item>
<item name="splitTrack">true</item>
<item name="switchTextAppearance">@style/TextAppearance.Material.Widget.Switch</item>
- <item name="textOn"></item>
- <item name="textOff"></item>
+ <item name="textOn">@string/capital_on</item>
+ <item name="textOff">@string/capital_off</item>
<item name="switchMinWidth">4dip</item>
<item name="switchPadding">4dip</item>
<item name="background">?attr/selectableItemBackgroundBorderless</item>
+ <item name="showText">false</item>
</style>
<style name="Widget.Material.EditText" parent="Widget.EditText"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5f4553b..7a871cc 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" />
@@ -1000,6 +1002,7 @@
<java-symbol type="array" name="config_callBarringMMI" />
<java-symbol type="array" name="config_globalActionsList" />
<java-symbol type="array" name="config_telephonyHardware" />
+ <java-symbol type="array" name="config_keySystemUuidMapping" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="indicator_input_error" />
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/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
index 2b75113..8e70e80 100644
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackFull.ttf b/data/fonts/DroidSansFallbackFull.ttf
index a9df005..68d2751 100644
--- a/data/fonts/DroidSansFallbackFull.ttf
+++ b/data/fonts/DroidSansFallbackFull.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackLegacy.ttf b/data/fonts/DroidSansFallbackLegacy.ttf
deleted file mode 100644
index 61460b1..0000000
--- a/data/fonts/DroidSansFallbackLegacy.ttf
+++ /dev/null
Binary files differ
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/google/play-services/games.jd b/docs/html/google/play-services/games.jd
index 94f6715..a73f688 100644
--- a/docs/html/google/play-services/games.jd
+++ b/docs/html/google/play-services/games.jd
@@ -1,4 +1,5 @@
page.title=Google Play Game Services
+page.tags="games"
header.hide=1
@jd:body
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/docs/html/tools/help/adb.jd b/docs/html/tools/help/adb.jd
index f980042..e2dd196 100644
--- a/docs/html/tools/help/adb.jd
+++ b/docs/html/tools/help/adb.jd
@@ -8,6 +8,7 @@
<div id="qv">
<h2>In this document</h2>
<ol>
+ <li><a href="#Enabling">Enabling adb Debugging</a></li>
<li><a href="#issuingcommands">Syntax</a></li>
<li><a href="#commandsummary">Commands</a></li>
<li><a href="#devicestatus">Querying for Emulator/Device Instances</a></li>
@@ -72,6 +73,19 @@
instance from any client (or from a script).</p>
+<h2 id="Enabling">Enabling adb Debugging</h2>
+
+<p>In order to use adb with a device connected over USB, you must enable
+<strong>USB debugging</strong> in the device system settings, under <strong>
+Developer options</strong>.</p>
+
+<p>On Android 4.2 and higher, the Developer options screen is
+hidden by default. To make it visible, go to
+<b>Settings > About phone</b> and tap <b>Build number</b> seven times. Return to the previous
+screen to find <strong>Developer options</strong> at the bottom.</p>
+
+<p>On some devices, the Developer options screen may be located or named differently.</p>
+
<p class="note"><strong>Note:</strong> When you connect a device running Android 4.2.2 or higher
to your computer, the system shows a dialog asking whether to accept an RSA key that allows
debugging through this computer. This security mechanism protects user devices because it ensures
@@ -80,6 +94,11 @@
SDK Platform-tools r16.0.1 and higher) in order to debug on a device running Android 4.2.2 or
higher.</p>
+<p>For more information about connecting to a device over USB, read
+<a href="{@docRoot}tools/device.html">Using Hardware Devices</a>.</p>
+
+
+
<h2 id="issuingcommands">Syntax</h2>
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 158801c..99596ef 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1712,7 +1712,8 @@
* the pos array.
*
* This method does not support glyph composition and decomposition and
- * should therefore not be used to render complex scripts.
+ * should therefore not be used to render complex scripts. It also doesn't
+ * handle supplementary characters (eg emoji).
*
* @param text The text to be drawn
* @param index The index of the first character to draw
@@ -1727,8 +1728,9 @@
if (index < 0 || index + count > text.length || count*2 > pos.length) {
throw new IndexOutOfBoundsException();
}
- native_drawPosText(mNativeCanvasWrapper, text, index, count, pos,
- paint.mNativePaint);
+ for (int i = 0; i < count; i++) {
+ drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
+ }
}
/**
@@ -1736,7 +1738,8 @@
* the pos array.
*
* This method does not support glyph composition and decomposition and
- * should therefore not be used to render complex scripts.
+ * should therefore not be used to render complex scripts. It also doesn't
+ * handle supplementary characters (eg emoji).
*
* @param text The text to be drawn
* @param pos Array of [x,y] positions, used to position each character
@@ -1744,10 +1747,7 @@
*/
@Deprecated
public void drawPosText(@NonNull String text, @NonNull float[] pos, @NonNull Paint paint) {
- if (text.length()*2 > pos.length) {
- throw new ArrayIndexOutOfBoundsException();
- }
- native_drawPosText(mNativeCanvasWrapper, text, pos, paint.mNativePaint);
+ drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
}
/**
@@ -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);
}
}
@@ -2009,23 +2009,16 @@
int start, int count, int contextStart, int contextCount,
float x, float y, boolean isRtl, long nativePaint, long nativeTypeface);
- private static native void native_drawPosText(long nativeCanvas,
- char[] text, int index,
- int count, float[] pos,
- long nativePaint);
- private static native void native_drawPosText(long nativeCanvas,
- String text, float[] pos,
- long nativePaint);
private static native void native_drawTextOnPath(long nativeCanvas,
char[] text, int index,
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/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index be940df..0a394d5 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -928,7 +928,7 @@
mTargetDensity = state.mTargetDensity;
}
- updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
computeBitmapSize();
}
}
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/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 005b8ef..28cd869 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -135,7 +135,6 @@
private Paint mStrokePaint; // optional, set by the caller
private ColorFilter mColorFilter; // optional, set by the caller
private int mAlpha = 0xFF; // modified by the caller
- private boolean mDither;
private final Path mPath = new Path();
private final RectF mRect = new RectF();
@@ -543,7 +542,7 @@
if (mLayerPaint == null) {
mLayerPaint = new Paint();
}
- mLayerPaint.setDither(mDither);
+ mLayerPaint.setDither(st.mDither);
mLayerPaint.setAlpha(mAlpha);
mLayerPaint.setColorFilter(mColorFilter);
@@ -561,14 +560,14 @@
individual paints
*/
mFillPaint.setAlpha(currFillAlpha);
- mFillPaint.setDither(mDither);
+ mFillPaint.setDither(st.mDither);
mFillPaint.setColorFilter(mColorFilter);
- if (mColorFilter != null && mGradientState.mColorStateList == null) {
+ if (mColorFilter != null && st.mColorStateList == null) {
mFillPaint.setColor(mAlpha << 24);
}
if (haveStroke) {
mStrokePaint.setAlpha(currStrokeAlpha);
- mStrokePaint.setDither(mDither);
+ mStrokePaint.setDither(st.mDither);
mStrokePaint.setColorFilter(mColorFilter);
}
}
@@ -804,8 +803,8 @@
@Override
public void setDither(boolean dither) {
- if (dither != mDither) {
- mDither = dither;
+ if (dither != mGradientState.mDither) {
+ mGradientState.mDither = dither;
invalidateSelf();
}
}
@@ -1015,7 +1014,7 @@
state.mThemeAttrs = a.extractThemeAttrs();
state.mShape = a.getInt(R.styleable.GradientDrawable_shape, state.mShape);
- mDither = a.getBoolean(R.styleable.GradientDrawable_dither, mDither);
+ state.mDither = a.getBoolean(R.styleable.GradientDrawable_dither, state.mDither);
if (state.mShape == RING) {
state.mInnerRadius = a.getDimensionPixelSize(
@@ -1459,6 +1458,8 @@
public float mThicknessRatio = DEFAULT_THICKNESS_RATIO;
public int mInnerRadius = -1;
public int mThickness = -1;
+ public boolean mDither = false;
+
private float mCenterX = 0.5f;
private float mCenterY = 0.5f;
private float mGradientRadius = 0.5f;
@@ -1510,6 +1511,7 @@
mThicknessRatio = state.mThicknessRatio;
mInnerRadius = state.mInnerRadius;
mThickness = state.mThickness;
+ mDither = state.mDither;
mCenterX = state.mCenterX;
mCenterY = state.mCenterY;
mGradientRadius = state.mGradientRadius;
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/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 0512ecc..f2e75a5 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -645,25 +645,29 @@
@Override
public Rect getDirtyBounds() {
- final Rect drawingBounds = mDrawingBounds;
- final Rect dirtyBounds = mDirtyBounds;
- dirtyBounds.set(drawingBounds);
- drawingBounds.setEmpty();
+ if (isProjected()) {
+ final Rect drawingBounds = mDrawingBounds;
+ final Rect dirtyBounds = mDirtyBounds;
+ dirtyBounds.set(drawingBounds);
+ drawingBounds.setEmpty();
- final int cX = (int) mHotspotBounds.exactCenterX();
- final int cY = (int) mHotspotBounds.exactCenterY();
- final Rect rippleBounds = mTempRect;
- final Ripple[] activeRipples = mAnimatingRipples;
- final int N = mAnimatingRipplesCount;
- for (int i = 0; i < N; i++) {
- activeRipples[i].getBounds(rippleBounds);
- rippleBounds.offset(cX, cY);
- drawingBounds.union(rippleBounds);
+ final int cX = (int) mHotspotBounds.exactCenterX();
+ final int cY = (int) mHotspotBounds.exactCenterY();
+ final Rect rippleBounds = mTempRect;
+ final Ripple[] activeRipples = mAnimatingRipples;
+ final int N = mAnimatingRipplesCount;
+ for (int i = 0; i < N; i++) {
+ activeRipples[i].getBounds(rippleBounds);
+ rippleBounds.offset(cX, cY);
+ drawingBounds.union(rippleBounds);
+ }
+
+ dirtyBounds.union(drawingBounds);
+ dirtyBounds.union(super.getDirtyBounds());
+ return dirtyBounds;
+ } else {
+ return getBounds();
}
-
- dirtyBounds.union(drawingBounds);
- dirtyBounds.union(super.getDirtyBounds());
- return dirtyBounds;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index beb300d..369bb59 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -416,6 +416,9 @@
final ShapeState state = mShapeState;
final Paint paint = state.mPaint;
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
int color = paint.getColor();
color = a.getColor(R.styleable.ShapeDrawable_color, color);
paint.setColor(color);
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c3c1bca..b4d1fdc 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -33,6 +33,7 @@
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.PathParser;
import android.util.Xml;
import com.android.internal.R;
@@ -147,10 +148,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);
@@ -954,7 +957,7 @@
}
- static class VPath {
+ private static class VPath {
private int[] mThemeAttrs;
int mStrokeColor = 0;
@@ -972,7 +975,7 @@
Paint.Join mStrokeLineJoin = Paint.Join.MITER;
float mStrokeMiterlimit = 4;
- private VNode[] mNode = null;
+ private PathParser.PathDataNode[] mNode = null;
private String mPathName;
public VPath() {
@@ -982,7 +985,7 @@
public void toPath(Path path) {
path.reset();
if (mNode != null) {
- VNode.createPath(mNode, path);
+ PathParser.PathDataNode.nodesToPath(mNode, path);
}
}
@@ -1097,7 +1100,8 @@
}
if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pathData] == 0) {
- mNode = parsePath(a.getString(R.styleable.VectorDrawablePath_pathData));
+ mNode = PathParser.createNodesFromPathData(a.getString(
+ R.styleable.VectorDrawablePath_pathData));
}
if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fill] == 0) {
@@ -1180,7 +1184,8 @@
}
if (a.hasValue(R.styleable.VectorDrawablePath_pathData)) {
- mNode = parsePath(a.getString(R.styleable.VectorDrawablePath_pathData));
+ mNode = PathParser.createNodesFromPathData(a.getString(
+ R.styleable.VectorDrawablePath_pathData));
}
mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor);
@@ -1216,488 +1221,5 @@
mStrokeColor = applyAlpha(mStrokeColor, mStrokeOpacity);
}
}
-
- private static int nextStart(String s, int end) {
- char c;
-
- while (end < s.length()) {
- c = s.charAt(end);
- if (((c - 'A') * (c - 'Z') <= 0) || (((c - 'a') * (c - 'z') <= 0))) {
- return end;
- }
- end++;
- }
- return end;
- }
-
- private void addNode(ArrayList<VectorDrawable.VNode> list, char cmd, float[] val) {
- list.add(new VectorDrawable.VNode(cmd, val));
- }
-
- /**
- * parse the floats in the string
- * this is an optimized version of
- * parseFloat(s.split(",|\\s"));
- *
- * @param s the string containing a command and list of floats
- * @return array of floats
- */
- private static float[] getFloats(String s) {
- if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
- return new float[0];
- }
- try {
- float[] tmp = new float[s.length()];
- int count = 0;
- int pos = 1, end;
- while ((end = extract(s, pos)) >= 0) {
- if (pos < end) {
- tmp[count++] = Float.parseFloat(s.substring(pos, end));
- }
- pos = end + 1;
- }
- // handle the final float if there is one
- if (pos < s.length()) {
- tmp[count++] = Float.parseFloat(s.substring(pos, s.length()));
- }
- return Arrays.copyOf(tmp, count);
- } catch (NumberFormatException e){
- Log.e(LOGTAG,"error in parsing \""+s+"\"");
- throw e;
- }
- }
-
- /**
- * calculate the position of the next comma or space
- * @param s the string to search
- * @param start the position to start searching
- * @return the position of the next comma or space or -1 if none found
- */
- private static int extract(String s, int start) {
- int space = s.indexOf(' ', start);
- int comma = s.indexOf(',', start);
- if (space == -1) {
- return comma;
- }
- if (comma == -1) {
- return space;
- }
- return (comma > space) ? space : comma;
- }
-
- private VectorDrawable.VNode[] parsePath(String value) {
- int start = 0;
- int end = 1;
-
- ArrayList<VectorDrawable.VNode> list = new ArrayList<VectorDrawable.VNode>();
- while (end < value.length()) {
- end = nextStart(value, end);
- String s = value.substring(start, end);
- float[] val = getFloats(s);
- addNode(list, s.charAt(0), val);
-
- start = end;
- end++;
- }
- if ((end - start) == 1 && start < value.length()) {
-
- addNode(list, value.charAt(start), new float[0]);
- }
- return list.toArray(new VectorDrawable.VNode[list.size()]);
- }
- }
-
- private static class VNode {
- private char mType;
- private float[] mParams;
-
- public VNode(char type, float[] params) {
- mType = type;
- mParams = params;
- }
-
- public VNode(VNode n) {
- mType = n.mType;
- mParams = Arrays.copyOf(n.mParams, n.mParams.length);
- }
-
- public static void createPath(VNode[] node, Path path) {
- float[] current = new float[4];
- char previousCommand = 'm';
- for (int i = 0; i < node.length; i++) {
- addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
- previousCommand = node[i].mType;
- }
- }
-
- private static void addCommand(Path path, float[] current,
- char previousCmd, char cmd, float[] val) {
-
- int incr = 2;
- float currentX = current[0];
- float currentY = current[1];
- float ctrlPointX = current[2];
- float ctrlPointY = current[3];
- float reflectiveCtrlPointX;
- float reflectiveCtrlPointY;
-
- switch (cmd) {
- case 'z':
- case 'Z':
- path.close();
- return;
- case 'm':
- case 'M':
- case 'l':
- case 'L':
- case 't':
- case 'T':
- incr = 2;
- break;
- case 'h':
- case 'H':
- case 'v':
- case 'V':
- incr = 1;
- break;
- case 'c':
- case 'C':
- incr = 6;
- break;
- case 's':
- case 'S':
- case 'q':
- case 'Q':
- incr = 4;
- break;
- case 'a':
- case 'A':
- incr = 7;
- break;
- }
- for (int k = 0; k < val.length; k += incr) {
- switch (cmd) {
- case 'm': // moveto - Start a new sub-path (relative)
- path.rMoveTo(val[k + 0], val[k + 1]);
- currentX += val[k + 0];
- currentY += val[k + 1];
- break;
- case 'M': // moveto - Start a new sub-path
- path.moveTo(val[k + 0], val[k + 1]);
- currentX = val[k + 0];
- currentY = val[k + 1];
- break;
- case 'l': // lineto - Draw a line from the current point (relative)
- path.rLineTo(val[k + 0], val[k + 1]);
- currentX += val[k + 0];
- currentY += val[k + 1];
- break;
- case 'L': // lineto - Draw a line from the current point
- path.lineTo(val[k + 0], val[k + 1]);
- currentX = val[k + 0];
- currentY = val[k + 1];
- break;
- case 'z': // closepath - Close the current subpath
- case 'Z': // closepath - Close the current subpath
- path.close();
- break;
- case 'h': // horizontal lineto - Draws a horizontal line (relative)
- path.rLineTo(val[k + 0], 0);
- currentX += val[k + 0];
- break;
- case 'H': // horizontal lineto - Draws a horizontal line
- path.lineTo(val[k + 0], currentY);
- currentX = val[k + 0];
- break;
- case 'v': // vertical lineto - Draws a vertical line from the current point (r)
- path.rLineTo(0, val[k + 0]);
- currentY += val[k + 0];
- break;
- case 'V': // vertical lineto - Draws a vertical line from the current point
- path.lineTo(currentX, val[k + 0]);
- currentY = val[k + 0];
- break;
- case 'c': // curveto - Draws a cubic Bézier curve (relative)
- path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
- val[k + 4], val[k + 5]);
-
- ctrlPointX = currentX + val[k + 2];
- ctrlPointY = currentY + val[k + 3];
- currentX += val[k + 4];
- currentY += val[k + 5];
-
- break;
- case 'C': // curveto - Draws a cubic Bézier curve
- path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
- val[k + 4], val[k + 5]);
- currentX = val[k + 4];
- currentY = val[k + 5];
- ctrlPointX = val[k + 2];
- ctrlPointY = val[k + 3];
- break;
- case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
- reflectiveCtrlPointX = 0;
- reflectiveCtrlPointY = 0;
- if (previousCmd == 'c' || previousCmd == 's'
- || previousCmd == 'C' || previousCmd == 'S') {
- reflectiveCtrlPointX = currentX - ctrlPointX;
- reflectiveCtrlPointY = currentY - ctrlPointY;
- }
- path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1],
- val[k + 2], val[k + 3]);
-
- ctrlPointX = currentX + val[k + 0];
- ctrlPointY = currentY + val[k + 1];
- currentX += val[k + 2];
- currentY += val[k + 3];
- break;
- case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
- reflectiveCtrlPointX = currentX;
- reflectiveCtrlPointY = currentY;
- if (previousCmd == 'c' || previousCmd == 's'
- || previousCmd == 'C' || previousCmd == 'S') {
- reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
- reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
- }
- path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = val[k + 0];
- ctrlPointY = val[k + 1];
- currentX = val[k + 2];
- currentY = val[k + 3];
- break;
- case 'q': // Draws a quadratic Bézier (relative)
- path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = currentX + val[k + 0];
- ctrlPointY = currentY + val[k + 1];
- currentX += val[k + 2];
- currentY += val[k + 3];
- break;
- case 'Q': // Draws a quadratic Bézier
- path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = val[k + 0];
- ctrlPointY = val[k + 1];
- currentX = val[k + 2];
- currentY = val[k + 3];
- break;
- case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
- reflectiveCtrlPointX = 0;
- reflectiveCtrlPointY = 0;
- if (previousCmd == 'q' || previousCmd == 't'
- || previousCmd == 'Q' || previousCmd == 'T') {
- reflectiveCtrlPointX = currentX - ctrlPointX;
- reflectiveCtrlPointY = currentY - ctrlPointY;
- }
- path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1]);
- ctrlPointX = currentX + reflectiveCtrlPointX;
- ctrlPointY = currentY + reflectiveCtrlPointY;
- currentX += val[k + 0];
- currentY += val[k + 1];
- break;
- case 'T': // Draws a quadratic Bézier curve (reflective control point)
- reflectiveCtrlPointX = currentX;
- reflectiveCtrlPointY = currentY;
- if (previousCmd == 'q' || previousCmd == 't'
- || previousCmd == 'Q' || previousCmd == 'T') {
- reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
- reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
- }
- path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1]);
- ctrlPointX = reflectiveCtrlPointX;
- ctrlPointY = reflectiveCtrlPointY;
- currentX = val[k + 0];
- currentY = val[k + 1];
- break;
- case 'a': // Draws an elliptical arc
- // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
- drawArc(path,
- currentX,
- currentY,
- val[k + 5] + currentX,
- val[k + 6] + currentY,
- val[k + 0],
- val[k + 1],
- val[k + 2],
- val[k + 3] != 0,
- val[k + 4] != 0);
- currentX += val[k + 5];
- currentY += val[k + 6];
- ctrlPointX = currentX;
- ctrlPointY = currentY;
- break;
- case 'A': // Draws an elliptical arc
- drawArc(path,
- currentX,
- currentY,
- val[k + 5],
- val[k + 6],
- val[k + 0],
- val[k + 1],
- val[k + 2],
- val[k + 3] != 0,
- val[k + 4] != 0);
- currentX = val[k + 5];
- currentY = val[k + 6];
- ctrlPointX = currentX;
- ctrlPointY = currentY;
- break;
- }
- previousCmd = cmd;
- }
- current[0] = currentX;
- current[1] = currentY;
- current[2] = ctrlPointX;
- current[3] = ctrlPointY;
- }
-
- private static void drawArc(Path p,
- float x0,
- float y0,
- float x1,
- float y1,
- float a,
- float b,
- float theta,
- boolean isMoreThanHalf,
- boolean isPositiveArc) {
-
- /* Convert rotation angle from degrees to radians */
- double thetaD = Math.toRadians(theta);
- /* Pre-compute rotation matrix entries */
- double cosTheta = Math.cos(thetaD);
- double sinTheta = Math.sin(thetaD);
- /* Transform (x0, y0) and (x1, y1) into unit space */
- /* using (inverse) rotation, followed by (inverse) scale */
- double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
- double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
- double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
- double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
-
- /* Compute differences and averages */
- double dx = x0p - x1p;
- double dy = y0p - y1p;
- double xm = (x0p + x1p) / 2;
- double ym = (y0p + y1p) / 2;
- /* Solve for intersecting unit circles */
- double dsq = dx * dx + dy * dy;
- if (dsq == 0.0) {
- Log.w(LOGTAG, " Points are coincident");
- return; /* Points are coincident */
- }
- double disc = 1.0 / dsq - 1.0 / 4.0;
- if (disc < 0.0) {
- Log.w(LOGTAG, "Points are too far apart " + dsq);
- float adjust = (float) (Math.sqrt(dsq) / 1.99999);
- drawArc(p, x0, y0, x1, y1, a * adjust,
- b * adjust, theta, isMoreThanHalf, isPositiveArc);
- return; /* Points are too far apart */
- }
- double s = Math.sqrt(disc);
- double sdx = s * dx;
- double sdy = s * dy;
- double cx;
- double cy;
- if (isMoreThanHalf == isPositiveArc) {
- cx = xm - sdy;
- cy = ym + sdx;
- } else {
- cx = xm + sdy;
- cy = ym - sdx;
- }
-
- double eta0 = Math.atan2((y0p - cy), (x0p - cx));
-
- double eta1 = Math.atan2((y1p - cy), (x1p - cx));
-
- double sweep = (eta1 - eta0);
- if (isPositiveArc != (sweep >= 0)) {
- if (sweep > 0) {
- sweep -= 2 * Math.PI;
- } else {
- sweep += 2 * Math.PI;
- }
- }
-
- cx *= a;
- cy *= b;
- double tcx = cx;
- cx = cx * cosTheta - cy * sinTheta;
- cy = tcx * sinTheta + cy * cosTheta;
-
- arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
- }
-
- /**
- * Converts an arc to cubic Bezier segments and records them in p.
- *
- * @param p The target for the cubic Bezier segments
- * @param cx The x coordinate center of the ellipse
- * @param cy The y coordinate center of the ellipse
- * @param a The radius of the ellipse in the horizontal direction
- * @param b The radius of the ellipse in the vertical direction
- * @param e1x E(eta1) x coordinate of the starting point of the arc
- * @param e1y E(eta2) y coordinate of the starting point of the arc
- * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
- * @param start The start angle of the arc on the ellipse
- * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
- */
- private static void arcToBezier(Path p,
- double cx,
- double cy,
- double a,
- double b,
- double e1x,
- double e1y,
- double theta,
- double start,
- double sweep) {
- // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
- // and http://www.spaceroots.org/documents/ellipse/node22.html
-
- // Maximum of 45 degrees per cubic Bezier segment
- int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
-
- double eta1 = start;
- double cosTheta = Math.cos(theta);
- double sinTheta = Math.sin(theta);
- double cosEta1 = Math.cos(eta1);
- double sinEta1 = Math.sin(eta1);
- double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
- double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
- double anglePerSegment = sweep / numSegments;
- for (int i = 0; i < numSegments; i++) {
- double eta2 = eta1 + anglePerSegment;
- double sinEta2 = Math.sin(eta2);
- double cosEta2 = Math.cos(eta2);
- double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
- double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
- double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
- double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
- double tanDiff2 = Math.tan((eta2 - eta1) / 2);
- double alpha =
- Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
- double q1x = e1x + alpha * ep1x;
- double q1y = e1y + alpha * ep1y;
- double q2x = e2x - alpha * ep2x;
- double q2y = e2y - alpha * ep2y;
-
- p.cubicTo((float) q1x,
- (float) q1y,
- (float) q2x,
- (float) q2y,
- (float) e2x,
- (float) e2y);
- eta1 = eta2;
- e1x = e2x;
- e1y = e2y;
- ep1x = ep2x;
- ep1y = ep2y;
- }
- }
-
}
}
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/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 77d16ab..8b32c40 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -170,6 +170,9 @@
}
const RenderProperties& props = frame->renderNode->properties();
+ if (props.getAlpha() <= 0) {
+ return;
+ }
// Perform clipping
if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
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 fc51170..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>
@@ -117,7 +118,7 @@
}
void RenderNode::damageSelf(TreeInfo& info) {
- if (isRenderable() && properties().getAlpha() > 0) {
+ if (isRenderable()) {
if (properties().getClipDamageToBounds()) {
info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
} else {
@@ -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/location/java/android/location/Location.java b/location/java/android/location/Location.java
index f70110c..bdd1195 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -583,7 +583,8 @@
}
/**
- * Get the altitude if available, in meters above sea level.
+ * Get the altitude if available, in meters above the WGS 84 reference
+ * ellipsoid.
*
* <p>If this location does not have an altitude then 0.0 is returned.
*/
@@ -592,7 +593,7 @@
}
/**
- * Set the altitude, in meters above sea level.
+ * Set the altitude, in meters above the WGS 84 reference ellipsoid.
*
* <p>Following this call {@link #hasAltitude} will return true.
*/
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 4b4be1b..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 */
@@ -155,6 +159,41 @@
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);
@@ -234,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
*/
@@ -246,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:
@@ -309,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..d35225a 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1616,26 +1616,6 @@
}
/**
- * @hide
- * If the stream is active locally or remotely, adjust its volume according to the enforced
- * priority rules.
- * Note: only AudioManager.STREAM_MUSIC is supported at the moment
- */
- public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
- if (streamType != STREAM_MUSIC) {
- Log.w(TAG, "adjustLocalOrRemoteStreamVolume() doesn't support stream " + streamType);
- }
- IAudioService service = getService();
- try {
- service.adjustLocalOrRemoteStreamVolume(streamType, direction,
- mContext.getOpPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in adjustLocalOrRemoteStreamVolume", e);
- }
- }
-
-
- /**
* Return a new audio session identifier not associated with any player or effect.
* It can for instance be used to create one of the {@link android.media.audiofx.AudioEffect}
* objects.
@@ -2534,6 +2514,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 +2954,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/AudioService.java b/media/java/android/media/AudioService.java
index 72f4a58..48479a4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -830,24 +830,6 @@
}
/** @see AudioManager#adjustVolume(int, int) */
- public void adjustVolume(int direction, int flags, String callingPackage) {
- adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags,
- callingPackage);
- }
-
- /** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
- * on streamType: fixed to STREAM_MUSIC */
- public void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
- String callingPackage) {
- if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
- if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
- adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0, callingPackage);
- } else if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
- mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
- }
- }
-
- /** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage) {
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
@@ -4482,22 +4464,6 @@
mMediaFocusControl.setPlaybackInfoForRcc(rccId, what, value);
}
- public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- if (DEBUG_SESSIONS) {
- int pid = getCallingPid();
- Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
- }
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
- }
-
- public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
- if (DEBUG_SESSIONS) {
- int pid = getCallingPid();
- Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
- }
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
- }
-
//==========================================================================================
// Audio Focus
//==========================================================================================
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/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c29e967..169631e 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -36,13 +36,8 @@
*/
interface IAudioService {
- void adjustVolume(int direction, int flags, String callingPackage);
-
boolean isLocalOrRemoteMusicActive();
- oneway void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
- String callingPackage);
-
void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage);
@@ -127,9 +122,6 @@
int getCurrentAudioFocus();
- oneway void dispatchMediaKeyEvent(in KeyEvent keyEvent);
- void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
-
void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
oneway void unregisterMediaButtonIntent(in PendingIntent pi);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e25714a..66175d0 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1826,11 +1826,7 @@
}
SubtitleTrack track = mInbandSubtitleTracks[index];
if (track != null) {
- long runID = data.getStartTimeUs() + 1;
- track.onData(data.getData(), true /* eos */, runID);
- track.setRunDiscardTimeMs(
- runID,
- (data.getStartTimeUs() + data.getDurationUs()) / 1000);
+ track.onData(data);
}
}
};
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
index b0e182d..9fedf63 100644
--- a/media/java/android/media/SubtitleTrack.java
+++ b/media/java/android/media/SubtitleTrack.java
@@ -75,6 +75,14 @@
private long mNextScheduledTimeMs = -1;
+ protected void onData(SubtitleData data) {
+ long runID = data.getStartTimeUs() + 1;
+ onData(data.getData(), true /* eos */, runID);
+ setRunDiscardTimeMs(
+ runID,
+ (data.getStartTimeUs() + data.getDurationUs()) / 1000);
+ }
+
/**
* Called when there is input data for the subtitle track. The
* complete subtitle for a track can include multiple whole units
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 5ca7daa..87a43e4 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -70,14 +70,7 @@
* @hide
*/
public static MediaController fromBinder(ISessionController sessionBinder) {
- MediaController controller = new MediaController(sessionBinder);
- try {
- controller.mSessionBinder.registerCallbackListener(controller.mCbStub);
- } catch (RemoteException e) {
- Log.wtf(TAG, "MediaController created with expired token", e);
- controller = null;
- }
- return controller;
+ return new MediaController(sessionBinder);
}
/**
@@ -305,7 +298,7 @@
mSessionBinder.registerCallbackListener(mCbStub);
mCbRegistered = true;
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in registerCallback", e);
+ Log.e(TAG, "Dead object in registerCallback", e);
}
}
}
@@ -314,14 +307,23 @@
if (cb == null) {
throw new IllegalArgumentException("Callback cannot be null");
}
+ boolean success = false;
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
MessageHandler handler = mCallbacks.get(i);
if (cb == handler.mCallback) {
mCallbacks.remove(i);
- return true;
+ success = true;
}
}
- return false;
+ if (mCbRegistered && mCallbacks.size() == 0) {
+ try {
+ mSessionBinder.unregisterCallbackListener(mCbStub);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in removeCallbackLocked");
+ }
+ mCbRegistered = false;
+ }
+ return success;
}
private MessageHandler getHandlerForCallbackLocked(Callback cb) {
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 3f0405d..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() {}
@@ -848,7 +969,7 @@
*
* @hide
*/
- public static final class WatchedPrograms implements BaseColumns {
+ public static final class WatchedPrograms implements BaseTvColumns {
/** The content:// style URI for this table. */
public static final Uri CONTENT_URI =
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/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 7dba21d..b6bb578 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -205,6 +205,37 @@
}
}
+ @SmallTest
+ public void testConnectLegacy() throws Exception {
+ final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+ ICamera cameraUser = null;
+ ICameraClient dummyCallbacks = new DummyCameraClient();
+
+ String clientPackageName = getContext().getPackageName();
+
+ BinderHolder holder = new BinderHolder();
+
+ try {
+ CameraBinderDecorator.newInstance(mUtils.getCameraService())
+ .connectLegacy(dummyCallbacks, cameraId, CAMERA_HAL_API_VERSION_1_0,
+ clientPackageName,
+ CameraBinderTestUtils.USE_CALLING_UID, holder);
+ cameraUser = ICamera.Stub.asInterface(holder.getBinder());
+ assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
+
+ Log.v(TAG, String.format("Camera %s connected as HAL1 legacy device", cameraId));
+ } catch (RuntimeException e) {
+ // Not all camera device support openLegacy.
+ Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
+ } finally {
+ if (cameraUser != null) {
+ cameraUser.disconnect();
+ }
+ }
+ }
+ }
+
static class DummyCameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
/*
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java
new file mode 100644
index 0000000..14bbe44
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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.mediaframeworktest.unit;
+
+import android.hardware.Camera;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+/**
+ * <pre>
+ * adb shell am instrument \
+ * -e class 'com.android.mediaframeworktest.unit.CameraOpenTest' \
+ * -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
+ * </pre>
+ */
+public class CameraOpenTest extends junit.framework.TestCase {
+ private static String TAG = "CameraOpenTest";
+
+ private Camera mCamera;
+
+ /**
+ * Test @hide android.hardware.Camera#openLegacy API that cannot be tested in CTS.
+ */
+ @SmallTest
+ public void testOpenLegacy() {
+ int nCameras = Camera.getNumberOfCameras();
+ for (int id = 0; id < nCameras; id++) {
+ try {
+ mCamera.openLegacy(id, Camera.CAMERA_HAL_API_VERSION_1_0);
+ } catch (RuntimeException e) {
+ Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
+ } finally {
+ if (mCamera != null) {
+ mCamera.release();
+ }
+ }
+ }
+ }
+}
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/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 8c9030d..e8944ec 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,14 +20,14 @@
<string name="title_open" msgid="4353228937663917801">"បើកពី"</string>
<string name="title_save" msgid="2433679664882857999">"រក្សាទុកទៅ"</string>
<string name="menu_create_dir" msgid="5947289605844398389">"បង្កើតថត"</string>
- <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string>
+ <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string>
<string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាពបញ្ជី"</string>
<string name="menu_sort" msgid="7677740407158414452">"តម្រៀបតាម"</string>
<string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string>
<string name="menu_settings" msgid="6008033148948428823">"ការកំណត់"</string>
<string name="menu_open" msgid="432922957274920903">"បើក"</string>
<string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string>
- <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
+ <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
<string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
<string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"បានជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string>
@@ -48,7 +48,7 @@
<string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញឧបករណ៍កម្រិតខ្ពស់"</string>
<string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញទំហំឯកសារ"</string>
<string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញទំហំឧបករណ៍"</string>
- <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
+ <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
<string name="toast_no_application" msgid="1339885974067891667">"មិនអាចបើកឯកសារ"</string>
<string name="toast_failed_delete" msgid="2180678019407244069">"មិនអាចលុបឯកសារមួយចំនួន"</string>
<string name="share_via" msgid="8966594246261344259">"ចែករំលែកតាម"</string>
diff --git a/packages/Keyguard/res/values-ca/strings.xml b/packages/Keyguard/res/values-ca/strings.xml
index d23d487..f258224 100644
--- a/packages/Keyguard/res/values-ca/strings.xml
+++ b/packages/Keyguard/res/values-ca/strings.xml
@@ -59,7 +59,7 @@
<string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"S\'ha iniciat la reorganització del widget."</string>
<string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Ha finalitzat la reorganització del widget."</string>
<string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"S\'ha suprimit el widget de <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
- <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Amplia l\'àrea de desbloqueig."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Desplega l\'àrea de desbloqueig."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueig lliscant el dit"</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueig mitjançant patró"</string>
<string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueig facial"</string>
diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml
index c26b1b4..ecdad8c 100644
--- a/packages/Keyguard/res/values-km-rKH/strings.xml
+++ b/packages/Keyguard/res/values-km-rKH/strings.xml
@@ -83,7 +83,7 @@
<string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
<string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូររបៀប"</string>
@@ -120,7 +120,7 @@
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string>
- <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index 7918755..38316ff 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -166,6 +166,11 @@
return info;
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
// DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
// This is an optimization to ensure we only recompute the patterns when the inputs change.
private static final class Patterns {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index 3e444fa..8945b15 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -341,11 +341,11 @@
}
// Volume buttons should only function for music (local or remote).
// TODO: Actually handle MUTE.
- mAudioManager.adjustLocalOrRemoteStreamVolume(
- AudioManager.STREAM_MUSIC,
+ mAudioManager.adjustSuggestedStreamVolume(
keyCode == KeyEvent.KEYCODE_VOLUME_UP
? AudioManager.ADJUST_RAISE
- : AudioManager.ADJUST_LOWER);
+ : AudioManager.ADJUST_LOWER /* direction */,
+ AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
// Don't execute default volume behavior
return true;
} else {
@@ -376,17 +376,13 @@
}
private void handleMediaKeyEvent(KeyEvent keyEvent) {
- IAudioService audioService = IAudioService.Stub.asInterface(
- ServiceManager.checkService(Context.AUDIO_SERVICE));
- if (audioService != null) {
- try {
- audioService.dispatchMediaKeyEvent(keyEvent);
- } catch (RemoteException e) {
- Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e);
+ synchronized (this) {
+ if (mAudioManager == null) {
+ mAudioManager = (AudioManager) getContext().getSystemService(
+ Context.AUDIO_SERVICE);
}
- } else {
- Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event");
}
+ mAudioManager.dispatchMediaKeyEvent(keyEvent);
}
@Override
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index cdcb21f..0146ab7 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -60,7 +60,7 @@
</plurals>
<string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
<string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើមឡើងវិញ"</string>
- <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
+ <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
<string name="reason_unknown" msgid="5507940196503246139">"មិនស្គាល់"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិនអាចប្រើបាន"</string>
<string-array name="color_mode_labels">
diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SystemUI/res/drawable/ic_account_circle.xml
index a7e8514..4a4c1c1 100644
--- a/packages/SystemUI/res/drawable/ic_account_circle.xml
+++ b/packages/SystemUI/res/drawable/ic_account_circle.xml
@@ -22,7 +22,13 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0"/>
+ <group
+ android:scaleX="1.2"
+ android:scaleY="1.2"
+ android:pivotX="12.0"
+ android:pivotY="12.0">
<path
android:fill="#FFFFFFFF"
android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,5.0c1.7,0.0 3.0,1.3 3.0,3.0c0.0,1.7 -1.3,3.0 -3.0,3.0c-1.7,0.0 -3.0,-1.3 -3.0,-3.0C9.0,6.3 10.3,5.0 12.0,5.0zM12.0,19.2c-2.5,0.0 -4.7,-1.3 -6.0,-3.2c0.0,-2.0 4.0,-3.1 6.0,-3.1c2.0,0.0 6.0,1.1 6.0,3.1C16.7,17.9 14.5,19.2 12.0,19.2z"/>
+ </group>
</vector>
diff --git a/packages/SystemUI/res/drawable/notification_scrim.xml b/packages/SystemUI/res/drawable/notification_scrim.xml
new file mode 100644
index 0000000..ff7e31f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_scrim.xml
@@ -0,0 +1,22 @@
+<?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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#34000000" />
+ <corners android:radius="@*android:dimen/notification_material_rounded_rect_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher.xml b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
new file mode 100644
index 0000000..5648065
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
@@ -0,0 +1,25 @@
+<?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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_user_switcher"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="end"
+ android:visibility="gone"
+ android:paddingTop="4dp">
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
new file mode 100644
index 0000000..691a80e
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -0,0 +1,40 @@
+<?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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:layout_marginEnd="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ android:background="@drawable/ripple_drawable">
+ <TextView android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="16dp"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
+ />
+ <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/picture"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:contentDescription="@null"
+ sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
+ sysui:activeFrameColor="@color/current_user_border_color" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index cde83bf..b54ba1a 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -24,6 +24,7 @@
android:id="@+id/notification_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@android:color/transparent"
>
<include
@@ -91,6 +92,14 @@
<include layout="@layout/status_bar_expanded_header" />
+ <ViewStub
+ android:id="@+id/keyguard_user_switcher"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
+ android:layout_gravity="end"
+ android:layout="@layout/keyguard_user_switcher" />
+
<include
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 353368b..2e4c0ef 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -73,9 +73,9 @@
android:layout_width="wrap_content"
android:layout_height="@dimen/status_bar_header_height"
android:layout_toStartOf="@id/multi_user_switch"
- android:layout_marginEnd="2dp"
+ android:layout_alignWithParentIfMissing="true"
android:layout_marginStart="16dp"
- />
+ android:paddingEnd="2dp" />
<TextView
android:id="@+id/header_charging_info"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index c442f79..f0f50e1 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -49,4 +49,10 @@
android:layout_width="120dp"
android:layout_height="wrap_content"
/>
+
+ <com.android.systemui.statusbar.NotificationScrimView
+ android:id="@+id/scrim_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
</com.android.systemui.statusbar.NotificationOverflowContainer>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 5fabd3e..7663d54 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -48,4 +48,9 @@
android:padding="2dp"
/>
+ <com.android.systemui.statusbar.NotificationScrimView
+ android:id="@+id/scrim_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
</com.android.systemui.statusbar.ExpandableNotificationRow>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 19b0f17..019fea0 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Avaa kamera pyyhkäisemällä oikealle"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Kunnes poistat tämän käytöstä"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ladataan (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kunnes täynnä)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Vieras"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Vieras"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Minuutiksi"</item>
<item quantity="other" msgid="6924190729213550991">"%d minuutiksi"</item>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index fcad161..d11c9be 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -244,7 +244,7 @@
<string name="phone_hint" msgid="3101468054914424646">"Balayer l\'écran vers la droite pour accéder au téléphone"</string>
<string name="camera_hint" msgid="5241441720959174226">"Balayer l\'écran vers la gauche pour accéder à l\'appareil photo"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
- <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"Invité"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"Ajouter un invité"</string>
<plurals name="zen_mode_duration_minutes">
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9f1722d..ddb7669 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -40,7 +40,7 @@
<string name="invalid_charger_text" msgid="5474997287953892710">"Utilizza solo il caricabatterie fornito in dotazione."</string>
<string name="battery_low_why" msgid="4553600287639198111">"Impostazioni"</string>
<string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Avviare risparmio batteria?"</string>
- <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Inizia"</string>
+ <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Avvia"</string>
<string name="battery_saver_start_action" msgid="7245333922937402896">"Avvia risparmio batteria"</string>
<string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Per aumentare la durata della batteria, Risparmio batteria riduce le prestazioni del tuo dispositivo.\n\nRisparmio batteria si disattiva quando il dispositivo è collegato alla corrente."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Impostazioni"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 8f1f30a..a35c821 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -42,7 +42,7 @@
<string name="battery_saver_confirmation_title" msgid="5987726159603849352">"გსურთ ბატარეის დამზოგის დაწყება?"</string>
<string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"დაწყება"</string>
<string name="battery_saver_start_action" msgid="7245333922937402896">"ბატარეის დამზოგის დაწყება"</string>
- <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"ბატარეის მოქმედების გასახანგრძლივებლად, ბატარეის დამზოგი შეამცირებს თქვენი მოწყობილობის წარმადობას.\n\nბატარეის დამზოგი გამოირთვება, როდესაც მოწყობილობას ელკვებაზე მიაერთებთ."</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"ბატარეის მოქმედების გასახანგრძლივებლად ბატარეის დამზოგი შეამცირებს თქვენი მოწყობილობის წარმადობას.\n\nბატარეის დამზოგი გამოირთვება, როდესაც მოწყობილობას ელკვებაზე მიაერთებთ."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"პარამეტრები"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"თვითმფრინავის რეჟიმი"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 88d9b64..29c5e5c 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -70,7 +70,7 @@
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុងរក្សាទុករូបថតអេក្រង់…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុងរក្សាទុករូបថតអេក្រង់..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថតអេក្រង់កំពុងត្រូវបានរក្សាទុក។"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string>
+ <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ដើម្បីមើលរូបថតអេក្រង់របស់អ្នក។"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"មិនអាចចាប់យករូបថតអេក្រង់។"</string>
<string name="screenshot_failed_text" msgid="8134011269572415402">"មិនអាចរក្សាទុករូបថតអេក្រង់។ ឧបករណ៍ផ្ទុកអាចកំពុងប្រើ។"</string>
@@ -147,7 +147,7 @@
<string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាតការជូនដំណឹង។"</string>
<string name="accessibility_gps_enabled" msgid="3511469499240123019">"បានបើក GPS ។"</string>
<string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល GPS ។"</string>
- <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បានបើកម៉ាស៊ីនអង្គុលីលេខ"</string>
+ <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បានបើកម៉ាស៊ីនអង្គុលីលេខ"</string>
<string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធីរោទ៍ញ័រ។"</string>
<string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធីរោទ៍ស្ងាត់។"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
@@ -197,7 +197,7 @@
<string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"បញ្ឈរ"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"ទេសភាព"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ត្របញ្ចូល"</string>
- <string name="quick_settings_location_label" msgid="5011327048748762257">"ទីតាំង"</string>
+ <string name="quick_settings_location_label" msgid="5011327048748762257">"ទីតាំង"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីតាំងបានបិទ"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណ៍មេឌៀ"</string>
<string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
@@ -221,11 +221,11 @@
<string name="recents_empty_message" msgid="7883614615463619450">"មិនមានកម្មវិធីថ្មីៗ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មានកម្មវិធី"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
- <string name="expanded_header_battery_charged" msgid="5945855970267657951">"បានបញ្ចូលថ្ម"</string>
+ <string name="expanded_header_battery_charged" msgid="5945855970267657951">"បានបញ្ចូលថ្ម"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"កំពុងបញ្ចូលថ្ម"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> រហូតដល់ពេញ"</string>
<string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"មិនកំពុងបញ្ចូលថ្ម"</string>
- <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string>
+ <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string>
<string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string>
<string name="description_direction_up" msgid="7169032478259485180">"រុញឡើងលើដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
<string name="description_direction_left" msgid="7207478719805562165">"រុញទៅឆ្វេងដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 3688c57..b765fb72 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -241,7 +241,7 @@
<string name="keyguard_unlock" msgid="8043466894212841998">"ເລື່ອນຂຶ້ນເພື່ອປົດລັອກ"</string>
<string name="phone_hint" msgid="3101468054914424646">"ປັດຂວາເພື່ອໃຊ້ໂທລະສັບ"</string>
<string name="camera_hint" msgid="5241441720959174226">"ປັດຊ້າຍເພື່ອໃຊ້ກ້ອງ"</string>
- <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າທ່ານຈະປິດ"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າທ່ານຈະປິດ"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ກຳລັງສາກໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າຈະເຕັມ)"</string>
<string name="guest_nickname" msgid="8059989128963789678">"ແຂກ"</string>
<string name="guest_new_guest" msgid="4259024453643879653">"+ ແຂກ"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 7d8a1ad..2fed4bd 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -32,16 +32,16 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Tiada pemberitahuan"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sedang berlangsung"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Pemberitahuan"</string>
- <string name="battery_low_title" msgid="6456385927409742437">"Bateri rendah"</string>
+ <string name="battery_low_title" msgid="6456385927409742437">"Bateri lemah"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"Berbaki <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"<xliff:g id="NUMBER">%d%%</xliff:g> yang tinggal. Penjimat bateri dihidupkan."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"Tinggal <xliff:g id="NUMBER">%d%%</xliff:g>. Penjimat bateri dihidupkan."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Pengecasan USB tidak disokong.\nGunakan hanya pengecas yang dibekalkan."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Pengecasan USB tidak disokong."</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"Gunakan pengecas yang dibekalkan sahaja."</string>
<string name="battery_low_why" msgid="4553600287639198111">"Tetapan"</string>
- <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Hidupkan penjimat bateri?"</string>
- <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Bermula"</string>
- <string name="battery_saver_start_action" msgid="7245333922937402896">"Hidupkan penjimat bateri"</string>
+ <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Mulakan penjimat bateri?"</string>
+ <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Mula"</string>
+ <string name="battery_saver_start_action" msgid="7245333922937402896">"Mulakan penjimat bateri"</string>
<string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Untuk membantu meningkatkan hayat bateri, penjimat Bateri akan mengurangkan prestasi peranti anda.\n\nPenjimat bateri akan dilumpuhkan apabila peranti anda disambungkan kepada sumber kuasa."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Tetapan"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Leret ke kiri untuk kamera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Sehingga anda matikan"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengecas (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Tetamu"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Tetamu"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Selama satu minit"</item>
<item quantity="other" msgid="6924190729213550991">"Selama %d minit"</item>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index c099dd9..8ec3621 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -243,10 +243,8 @@
<string name="camera_hint" msgid="5241441720959174226">"Glisați la stânga pentru a accesa camera foto"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Până la dezactivare"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Se încarcă (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"Invitat"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"+ Invitat"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Timp de un minut"</item>
<item quantity="other" msgid="6924190729213550991">"Timp de %d (de) minute"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 3bee434..d9c1cfc 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -42,7 +42,7 @@
<string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Желите ли да покренете Штедњу батерије?"</string>
<string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Покрени"</string>
<string name="battery_saver_start_action" msgid="7245333922937402896">"Покрените Штедњу батерије"</string>
- <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Да би продужила век трајања батерије, Штедња батерије ће умањити перформансе уређаја.\n\nШтедња батерије ће се искључити када укључите уређај."</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Да би продужила век трајања батерије, Штедња батерије умањује перформансе уређаја.\n\nШтедња батерије ће се искључити када прикључите уређај на напајање."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Подешавања"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Режим рада у авиону"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 47581a9..9f4c364 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -32,4 +32,7 @@
<!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
card. -->
<integer name="keyguard_max_notification_count">5</integer>
+
+ <!-- Set to true to enable the user switcher on the keyguard. -->
+ <bool name="config_keyguardUserSwitcher">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 7635c01..fbd8549f 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -42,7 +42,7 @@
<string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Pil tasarrufu başlatılsın mı?"</string>
<string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Başlat"</string>
<string name="battery_saver_start_action" msgid="7245333922937402896">"Pil tasarrufunu başlat"</string>
- <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Pil tasarrufu, pil ömrünü iyileştirmeye yardımcı olmak için cihazınızın performansını düşürür.\n\nCihazınız fişe takılıyken Pil tasarrufu devre dışı bırakılır."</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Pil tasarrufu, pil ömrünü iyileştirmeye yardımcı olmak için cihazınızın performansını düşürür.\n\nCihazınız fişe takıldığında Pil tasarrufu devre dışı bırakılır."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Ayarlar"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Kablosuz"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Uçak modu"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index e15d3e5..5c24e8b 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -42,7 +42,7 @@
<string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Khởi động trình tiết kiệm pin?"</string>
<string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Bắt đầu"</string>
<string name="battery_saver_start_action" msgid="7245333922937402896">"Khởi động trình tiết kiệm pin"</string>
- <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Để giúp tăng tuổi thọ pin, trình tiết kiệm pin sẽ giảm hiệu suất của thiết bị.\n\nTrình tiết kiệm pin sẽ tắt khi thiết bị của bạn được cắm vào."</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Để giúp tăng tuổi thọ pin, trình tiết kiệm pin sẽ giảm hiệu suất của thiết bị.\n\nTrình tiết kiệm pin sẽ tắt khi thiết bị của bạn được cắm vào nguồn điện."</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Cài đặt"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Chế độ trên máy bay"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index cfd4fe9..f975252 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -34,15 +34,15 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
<string name="battery_low_title" msgid="6456385927409742437">"电池电量偏低"</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"还剩 <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"电量还剩<xliff:g id="NUMBER">%d%%</xliff:g>。节电助手已启用。"</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"电量还剩<xliff:g id="NUMBER">%d%%</xliff:g>。节电助手已开启。"</string>
<string name="invalid_charger" msgid="4549105996740522523">"不支持 USB 充电功能。\n只能使用随附的充电器充电。"</string>
- <string name="invalid_charger_title" msgid="3515740382572798460">"不支持USB充电功能。"</string>
+ <string name="invalid_charger_title" msgid="3515740382572798460">"不支持USB充电。"</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"仅限使用设备随附的充电器。"</string>
<string name="battery_low_why" msgid="4553600287639198111">"设置"</string>
- <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"启动节电助手?"</string>
- <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"启动"</string>
- <string name="battery_saver_start_action" msgid="7245333922937402896">"启动节电助手"</string>
- <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"为了延长电池的续航时间,节电助手会减降设备的性能。\n\n设备接通电源后,节电助手会自动停用。"</string>
+ <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"要开启节电助手吗?"</string>
+ <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"开启"</string>
+ <string name="battery_saver_start_action" msgid="7245333922937402896">"开启节电助手"</string>
+ <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"为了延长电池的续航时间,节电助手会减降设备的性能。\n\n设备接通电源后,节电助手会自动关闭。"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"设置"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"WLAN"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"飞行模式"</string>
@@ -245,10 +245,8 @@
<string name="camera_hint" msgid="5241441720959174226">"向左滑动可打开相机"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"直到您将其关闭"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"正在充电(还需<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>才能充满)"</string>
- <!-- no translation found for guest_nickname (8059989128963789678) -->
- <skip />
- <!-- no translation found for guest_new_guest (4259024453643879653) -->
- <skip />
+ <string name="guest_nickname" msgid="8059989128963789678">"访客"</string>
+ <string name="guest_new_guest" msgid="4259024453643879653">"添加新访客"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1分钟"</item>
<item quantity="other" msgid="6924190729213550991">"%d分钟"</item>
@@ -257,7 +255,7 @@
<item quantity="one" msgid="3480040795582254384">"1小时"</item>
<item quantity="other" msgid="5408537517529822157">"%d小时"</item>
</plurals>
- <string name="battery_saver_notification_title" msgid="237918726750955859">"节电助手已启用"</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"节电助手已开启"</string>
<string name="battery_saver_notification_text" msgid="7796554871101546872">"设备性能已减降。"</string>
<string name="battery_saver_notification_action_text" msgid="7546297220816993504">"打开节电助手设置"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index c453618..8473d96 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -54,5 +54,10 @@
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
+ <declare-styleable name="UserAvatarView">
+ <attr name="frameWidth" format="dimension" />
+ <attr name="activeFrameColor" format="color" />
+ <attr name="frameColor" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8c1a9c7..4e38da6 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. -->
@@ -62,6 +60,9 @@
<color name="keyguard_affordance">#ffffffff</color>
+ <!-- The color of the circle around the primary user in the user switcher -->
+ <color name="current_user_border_color">@color/primary_color</color>
+
<!-- Our material color palette (deep teal) -->
<color name="primary_color">#ff7fcac3</color>
<color name="background_color_1">#ff384248</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c64a182..48b327d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -154,5 +154,8 @@
Notification.tickerText across the status bar for what seems like an
eternity. -->
<bool name="enable_ticker">false</bool>
+
+ <!-- Set to true to enable the user switcher on the keyguard. -->
+ <bool name="config_keyguardUserSwitcher">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bbcc9c1..36c1994 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -320,6 +320,9 @@
device. -->
<dimen name="unlock_move_distance">75dp</dimen>
+ <!-- Distance after which the scrim starts fading in when dragging down the quick settings -->
+ <dimen name="notification_scrim_wait_distance">100dp</dimen>
+
<!-- Move distance for the unlock hint animation on the lockscreen -->
<dimen name="hint_move_distance">75dp</dimen>
@@ -332,4 +335,11 @@
<!-- end margin for multi user switch in expanded quick settings -->
<dimen name="multi_user_switch_expanded_margin">8dp</dimen>
+
+ <!-- end margin for system icons if multi user switch is hidden -->
+ <dimen name="system_icons_switcher_hidden_expanded_margin">16dp</dimen>
+
+ <!-- The thickness of the colored border around the current user. -->
+ <dimen name="keyguard_user_switcher_border_thickness">2dp</dimen>
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index c117eba..e5d5b03 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -100,6 +100,13 @@
<style name="TextAppearance.StatusBar.Expanded.Network.EmergencyOnly">
</style>
+ <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
+ <item name="android:textSize">16sp</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#ffffff</item>
+ </style>
+ <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" />
+
<style name="TextAppearance" />
<style name="TextAppearance.QuickSettings" />
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c3ba349..41b1f75 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -152,6 +152,7 @@
.setContentText(mContext.getString(R.string.invalid_charger_text))
.setPriority(Notification.PRIORITY_MAX)
.setCategory(Notification.CATEGORY_SYSTEM)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
.setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_CHARGER), true);
final Notification n = nb.build();
if (n.headsUpContentView != null) {
@@ -171,6 +172,7 @@
.setOngoing(true)
.setPriority(Notification.PRIORITY_MAX)
.setCategory(Notification.CATEGORY_SYSTEM)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
.setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_WARNING), true);
if (hasBatterySettings()) {
nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
@@ -197,7 +199,8 @@
.setContentText(mContext.getString(R.string.battery_saver_notification_text))
.setOngoing(true)
.setShowWhen(false)
- .setCategory(Notification.CATEGORY_SYSTEM);
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasSaverSettings()) {
nb.addAction(0,
mContext.getString(R.string.battery_saver_notification_action_text),
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/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 8d19f50..f6f78e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -119,6 +119,7 @@
private NotificationBackgroundView mBackgroundNormal;
private NotificationBackgroundView mBackgroundDimmed;
+ private NotificationScrimView mScrimView;
private ObjectAnimator mBackgroundAnimator;
private RectF mAppearAnimationRect = new RectF();
private PorterDuffColorFilter mAppearAnimationFilter;
@@ -153,6 +154,7 @@
mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
updateBackground();
updateBackgroundResources();
+ mScrimView = (NotificationScrimView) findViewById(R.id.scrim_view);
}
private final Runnable mTapTimeoutRunnable = new Runnable() {
@@ -379,6 +381,7 @@
setPivotY(actualHeight / 2);
mBackgroundNormal.setActualHeight(actualHeight);
mBackgroundDimmed.setActualHeight(actualHeight);
+ mScrimView.setActualHeight(actualHeight);
}
@Override
@@ -386,6 +389,7 @@
super.setClipTopAmount(clipTopAmount);
mBackgroundNormal.setClipTopAmount(clipTopAmount);
mBackgroundDimmed.setClipTopAmount(clipTopAmount);
+ mScrimView.setClipTopAmount(clipTopAmount);
}
@Override
@@ -405,6 +409,11 @@
}
}
+ @Override
+ public void setScrimAmount(float scrimAmount) {
+ mScrimView.setAlpha(scrimAmount);
+ }
+
private void startAppearAnimation(boolean isAppearing,
float translationDirection, long delay, final Runnable onFinishedRunnable) {
if (mAppearAnimator != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index ac2537c..4d4a8ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -223,6 +223,8 @@
public abstract void performAddAnimation(long delay);
+ public abstract void setScrimAmount(float scrimAmount);
+
/**
* A listener notifying when {@link #getActualHeight} changes.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationScrimView.java
new file mode 100644
index 0000000..440b2c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationScrimView.java
@@ -0,0 +1,79 @@
+/*
+ * 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.systemui.statusbar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.keyguard.R;
+
+/**
+ * A view that can be used for both the dimmed and normal background of an notification.
+ */
+public class NotificationScrimView extends View {
+
+ private Drawable mBackground;
+ private int mClipTopAmount;
+ private int mActualHeight;
+
+ public NotificationScrimView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mBackground = getResources().getDrawable(R.drawable.notification_scrim);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ draw(canvas, mBackground);
+ }
+
+ private void draw(Canvas canvas, Drawable drawable) {
+ if (drawable != null) {
+ drawable.setBounds(0, mClipTopAmount, getWidth(), mActualHeight);
+ drawable.draw(canvas);
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || who == mBackground;
+ }
+
+ public void setActualHeight(int actualHeight) {
+ mActualHeight = actualHeight;
+ invalidate();
+ }
+
+ public int getActualHeight() {
+ return mActualHeight;
+ }
+
+ public void setClipTopAmount(int clipTopAmount) {
+ mClipTopAmount = clipTopAmount;
+ invalidate();
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+
+ // Prevents this view from creating a layer when alpha is animating.
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
index f80f0fd..650abaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
@@ -121,4 +121,9 @@
public void performAddAnimation(long delay) {
performVisibilityAnimation(true);
}
+
+ @Override
+ public void setScrimAmount(float scrimAmount) {
+ // We don't need to scrim the speedbumps
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 97aa993..63698e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -58,6 +58,7 @@
private ImageView mCameraImageView;
private ImageView mPhoneImageView;
private ImageView mLockIcon;
+ private View mIndicationText;
private ActivityStarter mActivityStarter;
private UnlockMethodCache mUnlockMethodCache;
@@ -87,6 +88,7 @@
mCameraImageView = (ImageView) findViewById(R.id.camera_button);
mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
mLockIcon = (ImageView) findViewById(R.id.lock_icon);
+ mIndicationText = findViewById(R.id.keyguard_indication_text);
watchForCameraPolicyChanges();
watchForAccessibilityChanges();
updateCameraVisibility();
@@ -231,6 +233,10 @@
return mLockIcon;
}
+ public View getIndicationView() {
+ return mIndicationText;
+ }
+
@Override
public void onMethodSecureChanged(boolean methodSecure) {
updateTrust();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 6a83a5e..319096d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -18,6 +18,7 @@
import android.content.res.Resources;
import android.graphics.Path;
+import android.view.animation.AccelerateInterpolator;
import android.view.animation.PathInterpolator;
import com.android.systemui.R;
@@ -31,6 +32,10 @@
private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
+ private static final float CLOCK_SCALE_FADE_START = 0.95f;
+ private static final float CLOCK_SCALE_FADE_END = 0.75f;
+ private static final float CLOCK_SCALE_FADE_END_NO_NOTIFS = 0.5f;
+
private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
@@ -61,6 +66,8 @@
sSlowDownInterpolator = new PathInterpolator(path);
}
+ private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
+
/**
* Refreshes the dimension values.
*/
@@ -87,18 +94,29 @@
}
public void run(Result result) {
- int y = getClockY() - mKeyguardStatusHeight/2;
+ int y = getClockY() - mKeyguardStatusHeight / 2;
float clockAdjustment = getClockYExpansionAdjustment();
float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
int clockNotificationsPadding = getClockNotificationsPadding()
+ result.stackScrollerPaddingAdjustment;
int padding = y + clockNotificationsPadding;
- y += clockAdjustment;
result.clockY = y;
result.stackScrollerPadding = mKeyguardStatusHeight + padding;
- result.clockAlpha = getClockAlpha(result.stackScrollerPadding
- - (y + mKeyguardStatusHeight));
+ result.clockScale = getClockScale(result.stackScrollerPadding,
+ result.clockY,
+ y + getClockNotificationsPadding() + mKeyguardStatusHeight);
+ result.clockAlpha = getClockAlpha(result.clockScale);
+ }
+
+ private float getClockScale(int notificationPadding, int clockY, int startPadding) {
+ float scaleMultiplier = getNotificationAmountT() == 0 ? 6.0f : 5.0f;
+ float scaleEnd = clockY - mKeyguardStatusHeight * scaleMultiplier;
+ float distanceToScaleEnd = notificationPadding - scaleEnd;
+ float progress = distanceToScaleEnd / (startPadding - scaleEnd);
+ progress = Math.max(0.0f, Math.min(progress, 1.0f));
+ progress = mAccelerateInterpolator.getInterpolation(progress);
+ return progress;
}
private int getClockNotificationsPadding() {
@@ -144,11 +162,12 @@
+ t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
}
- private float getClockAlpha(int clockNotificationPadding) {
- float t = getNotificationAmountT();
- t = (float) Math.pow(t, 0.3f);
- float multiplier = 1 + 2 * (1 - t);
- float alpha = 1 + (float) clockNotificationPadding * multiplier / mKeyguardStatusHeight * 3;
+ private float getClockAlpha(float scale) {
+ float fadeEnd = getNotificationAmountT() == 0.0f
+ ? CLOCK_SCALE_FADE_END_NO_NOTIFS
+ : CLOCK_SCALE_FADE_END;
+ float alpha = (scale - fadeEnd)
+ / (CLOCK_SCALE_FADE_START - fadeEnd);
return Math.max(0, Math.min(1, alpha));
}
@@ -168,6 +187,11 @@
public int clockY;
/**
+ * The scale of the Clock
+ */
+ public float clockScale;
+
+ /**
* The alpha value of the clock.
*/
public float clockAlpha;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index dde95bf..a6ce5d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableView;
@@ -48,6 +49,11 @@
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
KeyguardPageSwipeHelper.Callback {
+ // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
+ // changed.
+ private static final int CAP_HEIGHT = 1456;
+ private static final int FONT_HEIGHT = 2163;
+
private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
private KeyguardPageSwipeHelper mPageSwiper;
@@ -56,7 +62,7 @@
private View mQsPanel;
private View mKeyguardStatusView;
private ObservableScrollView mScrollView;
- private View mStackScrollerContainer;
+ private TextView mClockView;
private NotificationStackScrollLayout mNotificationStackScroller;
private int mNotificationTopPadding;
@@ -105,9 +111,11 @@
new KeyguardClockPositionAlgorithm.Result();
private boolean mIsSwipedHorizontally;
private boolean mIsExpanding;
- private KeyguardBottomAreaView mKeyguardBottomArea;
+
private boolean mBlockTouches;
private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
+ private int mNotificationScrimWaitDistance;
+ private boolean mOnNotificationsOnDown;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -135,9 +143,9 @@
mHeader.getBackgroundView().setOnClickListener(this);
mHeader.setOverlayParent(this);
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
- mStackScrollerContainer = findViewById(R.id.notification_container_parent);
mQsContainer = findViewById(R.id.quick_settings_container);
mQsPanel = findViewById(R.id.quick_settings_panel);
+ mClockView = (TextView) findViewById(R.id.clock_view);
mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
mScrollView.setListener(this);
mNotificationStackScroller = (NotificationStackScrollLayout)
@@ -169,12 +177,19 @@
getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
mClockPositionAlgorithm.loadDimens(getResources());
+ mNotificationScrimWaitDistance =
+ getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
+ // Update Clock Pivot
+ mKeyguardStatusView.setPivotX(getWidth() / 2);
+ mKeyguardStatusView.setPivotY(
+ (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mClockView.getTextSize());
+
// Calculate quick setting heights.
mQsMinExpansionHeight = mHeader.getCollapsedHeight() + mQsPeekHeight;
mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
@@ -197,7 +212,7 @@
* showing.
*/
private void positionClockAndNotifications() {
- boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+ boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
int bottom = mStackScrollerOverscrolling
@@ -215,17 +230,17 @@
getHeight(),
mKeyguardStatusView.getHeight());
mClockPositionAlgorithm.run(mClockPositionResult);
- if (animateClock || mClockAnimator != null) {
+ if (animate || mClockAnimator != null) {
startClockAnimation(mClockPositionResult.clockY);
} else {
mKeyguardStatusView.setY(mClockPositionResult.clockY);
}
- applyClockAlpha(mClockPositionResult.clockAlpha);
+ updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale);
stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
}
mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
- requestScrollerTopPaddingUpdate(animateClock);
+ requestScrollerTopPaddingUpdate(animate);
}
private void startClockAnimation(int y) {
@@ -258,13 +273,10 @@
});
}
- private void applyClockAlpha(float alpha) {
- if (alpha != 1.0f) {
- mKeyguardStatusView.setLayerType(LAYER_TYPE_HARDWARE, null);
- } else {
- mKeyguardStatusView.setLayerType(LAYER_TYPE_NONE, null);
- }
+ private void updateClock(float alpha, float scale) {
mKeyguardStatusView.setAlpha(alpha);
+ mKeyguardStatusView.setScaleX(scale);
+ mKeyguardStatusView.setScaleY(scale);
}
public void animateToFullShade() {
@@ -344,6 +356,7 @@
mInitialTouchX = x;
initVelocityTracker();
trackMovement(event);
+ mOnNotificationsOnDown = isOnNotifications(x, y);
if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
@@ -391,6 +404,8 @@
if (mQsTracking) {
flingQsWithCurrentVelocity();
mQsTracking = false;
+ } else if (mQsFullyExpanded && mOnNotificationsOnDown) {
+ flingSettings(0 /* vel */, false /* expand */);
}
mIntercepting = false;
break;
@@ -398,6 +413,10 @@
return !mQsExpanded && super.onInterceptTouchEvent(event);
}
+ private boolean isOnNotifications(float x, float y) {
+ return mNotificationStackScroller.getChildAtPosition(x, y) != null;
+ }
+
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -577,9 +596,17 @@
mHeader.setExpansion(height - mQsPeekHeight);
setQsTranslation(height);
requestScrollerTopPaddingUpdate(false /* animate */);
+ updateNotificationScrim(height);
mStatusBar.userActivity();
}
+ private void updateNotificationScrim(float height) {
+ int startDistance = mQsMinExpansionHeight + mNotificationScrimWaitDistance;
+ float progress = (height - startDistance) / (mQsMaxExpansionHeight - startDistance);
+ progress = Math.max(0.0f, Math.min(progress, 1.0f));
+ mNotificationStackScroller.setScrimAlpha(progress);
+ }
+
private void setQsTranslation(float height) {
mQsContainer.setY(height - mQsContainer.getHeight());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 1f3098d..12aa004 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -62,6 +62,7 @@
protected int mTouchSlop;
protected boolean mHintAnimationRunning;
private boolean mOverExpandedBeforeFling;
+ private float mOriginalIndicationY;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -82,6 +83,7 @@
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mBounceInterpolator;
+ protected KeyguardBottomAreaView mKeyguardBottomArea;
protected void onExpandingFinished() {
mBar.onExpandingFinished();
@@ -652,6 +654,22 @@
});
animator.start();
mHeightAnimator = animator;
+ mOriginalIndicationY = mKeyguardBottomArea.getIndicationView().getY();
+ mKeyguardBottomArea.getIndicationView().animate()
+ .y(mOriginalIndicationY - mHintDistance)
+ .setDuration(250)
+ .setInterpolator(mLinearOutSlowInInterpolator)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardBottomArea.getIndicationView().animate()
+ .y(mOriginalIndicationY)
+ .setDuration(450)
+ .setInterpolator(mBounceInterpolator)
+ .start();
+ }
+ })
+ .start();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 1da7dab..d52377e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -82,7 +82,6 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
-import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
@@ -123,6 +122,7 @@
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.DateView;
import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
@@ -211,6 +211,7 @@
ZenModeController mZenModeController;
CastControllerImpl mCastController;
VolumeComponent mVolumeComponent;
+ KeyguardUserSwitcher mKeyguardUserSwitcher;
int mNaturalBarHeight = -1;
int mIconSize = -1;
@@ -260,6 +261,10 @@
boolean mLeaveOpenOnKeyguardHide;
KeyguardIndicationController mKeyguardIndicationController;
+ private boolean mKeyguardFadingAway;
+ private long mKeyguardFadingAwayDelay;
+ private long mKeyguardFadingAwayDuration;
+
int mKeyguardMaxNotificationCount;
View mDateTimeView;
@@ -399,7 +404,9 @@
private boolean mSettingsCancelled;
private boolean mSettingsClosing;
private boolean mVisible;
+ private boolean mWaitingForKeyguardExit;
+ private Interpolator mLinearOutSlowIn;
private Interpolator mAlphaOut = new PathInterpolator(0f, 0.4f, 1f, 1f);
private Interpolator mAlphaIn = new PathInterpolator(0f, 0f, 0.8f, 1f);
@@ -713,6 +720,8 @@
final SignalClusterView signalCluster =
(SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
+ mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
+ (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader);
mNetworkController.addSignalCluster(signalCluster);
signalCluster.setNetworkController(mNetworkController);
@@ -1453,7 +1462,7 @@
}
private int adjustDisableFlags(int state) {
- if (mExpandedVisible) {
+ if (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit) {
state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
state |= StatusBarManager.DISABLE_SYSTEM_INFO;
}
@@ -1501,19 +1510,9 @@
if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
mSystemIconArea.animate().cancel();
if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
- mSystemIconArea.animate()
- .alpha(0f)
- .withLayer()
- .setDuration(160)
- .setInterpolator(mAlphaIn)
- .setListener(mMakeIconsInvisible);
+ animateStatusBarHide(mSystemIconArea);
} else {
- mSystemIconArea.setVisibility(View.VISIBLE);
- mSystemIconArea.animate()
- .alpha(1f)
- .withLayer()
- .setInterpolator(mAlphaOut)
- .setDuration(320);
+ animateStatusBarShow(mSystemIconArea);
}
}
@@ -1546,25 +1545,48 @@
if (mTicking) {
haltTicker();
}
-
- mNotificationIcons.animate()
- .alpha(0f)
- .withLayer()
- .setDuration(160)
- .setInterpolator(mAlphaIn)
- .setListener(mMakeIconsInvisible)
- .start();
+ animateStatusBarHide(mNotificationIcons);
} else {
- mNotificationIcons.setVisibility(View.VISIBLE);
- mNotificationIcons.animate()
- .alpha(1f)
- .withLayer()
- .setInterpolator(mAlphaOut)
- .setDuration(320);
+ animateStatusBarShow(mNotificationIcons);
}
}
}
+ /**
+ * Animates {@code v}, a view that is part of the status bar, out.
+ */
+ private void animateStatusBarHide(View v) {
+ v.animate()
+ .alpha(0f)
+ .withLayer()
+ .setDuration(160)
+ .setInterpolator(mAlphaIn)
+ .setStartDelay(0)
+ .setListener(mMakeIconsInvisible)
+ .start();
+ }
+
+ /**
+ * Animates {@code v}, a view that is part of the status bar, in.
+ */
+ private void animateStatusBarShow(View v) {
+ v.setVisibility(View.VISIBLE);
+ v.animate()
+ .alpha(1f)
+ .withLayer()
+ .setInterpolator(mAlphaOut)
+ .setDuration(320)
+ .setStartDelay(0);
+
+ // Synchronize the motion with the Keyguard fading if necessary.
+ if (mKeyguardFadingAway) {
+ v.animate()
+ .setDuration(mKeyguardFadingAwayDuration)
+ .setInterpolator(mLinearOutSlowIn)
+ .setStartDelay(mKeyguardFadingAwayDelay);
+ }
+ }
+
@Override
protected BaseStatusBar.H createHandler() {
return new PhoneStatusBar.H();
@@ -1666,6 +1688,7 @@
mStatusBarWindowManager.setStatusBarExpanded(true);
visibilityChanged(true);
+ mWaitingForKeyguardExit = false;
disable(mDisabledUnmodified);
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
@@ -1859,8 +1882,8 @@
}
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
- disable(mDisabledUnmodified);
showBouncer();
+ disable(mDisabledUnmodified);
}
public boolean interceptTouchEvent(MotionEvent event) {
@@ -2574,6 +2597,8 @@
if (mQSPanel != null) mQSPanel.updateResources();
loadDimens();
+ mLinearOutSlowIn = AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.linear_out_slow_in);
}
protected void loadDimens() {
@@ -2901,6 +2926,27 @@
updateKeyguardState();
}
+ /**
+ * Notifies the status bar the Keyguard is fading away with the specified timings.
+ *
+ * @param delay the animation delay in miliseconds
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds
+ */
+ public void setKeyguardFadingAway(long delay, long fadeoutDuration) {
+ mKeyguardFadingAway = true;
+ mKeyguardFadingAwayDelay = delay;
+ mKeyguardFadingAwayDuration = fadeoutDuration;
+ mWaitingForKeyguardExit = false;
+ disable(mDisabledUnmodified);
+ }
+
+ /**
+ * Notifies that the Keyguard fading away animation is done.
+ */
+ public void finishKeyguardFadingAway() {
+ mKeyguardFadingAway = false;
+ }
+
private void updatePublicMode() {
setLockscreenPublicMode(
(mStatusBarKeyguardViewManager.isShowing() ||
@@ -2913,9 +2959,11 @@
mKeyguardStatusView.setVisibility(View.VISIBLE);
mKeyguardIndicationController.setVisible(true);
mNotificationPanel.resetViews();
+ mKeyguardUserSwitcher.setKeyguard(true);
} else {
mKeyguardStatusView.setVisibility(View.GONE);
mKeyguardIndicationController.setVisible(false);
+ mKeyguardUserSwitcher.setKeyguard(false);
}
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
mKeyguardBottomArea.setVisibility(View.VISIBLE);
@@ -2975,6 +3023,7 @@
private void showBouncer() {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+ mWaitingForKeyguardExit = true;
mStatusBarKeyguardViewManager.dismiss();
}
}
@@ -3125,6 +3174,12 @@
mSystemIconArea.addView(mSystemIcons, 0);
}
+ @Override
+ public void setBouncerShowing(boolean bouncerShowing) {
+ super.setBouncerShowing(bouncerShowing);
+ disable(mDisabledUnmodified);
+ }
+
public void onScreenTurnedOff() {
mStackScroller.setAnimationsEnabled(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 1712124..fc10a08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -63,6 +63,7 @@
private boolean mShowEmergencyCallsOnly;
private boolean mShowChargingInfo;
+ private boolean mKeyguardUserSwitcherShowing;
private int mCollapsedHeight;
private int mExpandedHeight;
@@ -72,6 +73,7 @@
private int mNormalWidth;
private int mPadding;
private int mMultiUserExpandedMargin;
+ private int mSystemIconsSwitcherHiddenExpandedMargin;
private ActivityStarter mActivityStarter;
private BrightnessController mBrightnessController;
@@ -125,7 +127,8 @@
mPadding = getResources().getDimensionPixelSize(R.dimen.notification_side_padding);
mMultiUserExpandedMargin =
getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
-
+ mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
+ R.dimen.system_icons_switcher_hidden_expanded_margin);
}
public void setActivityStarter(ActivityStarter activityStarter) {
@@ -216,12 +219,15 @@
? VISIBLE : GONE);
mChargingInfo.setVisibility(mExpanded && !mOverscrolled && mShowChargingInfo
&& !mShowEmergencyCallsOnly ? VISIBLE : GONE);
+ mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing
+ ? VISIBLE : GONE);
}
private void updateSystemIconsLayoutParams() {
RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams();
boolean systemIconsAboveClock = mExpanded && !mOverscrolled
&& mShowChargingInfo && !mShowEmergencyCallsOnly;
+ lp.setMarginEnd(0);
if (systemIconsAboveClock) {
lp.addRule(ALIGN_PARENT_START);
lp.removeRule(START_OF);
@@ -230,7 +236,11 @@
? mSettingsButton.getId()
: mMultiUserSwitch.getId());
lp.removeRule(ALIGN_PARENT_START);
+ if (mMultiUserSwitch.getVisibility() == GONE) {
+ lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
+ }
}
+ mSystemIconsContainer.setLayoutParams(lp);
RelativeLayout.LayoutParams clockLp = (LayoutParams) mDateTime.getLayoutParams();
if (systemIconsAboveClock) {
@@ -238,6 +248,7 @@
} else {
clockLp.addRule(BELOW, mEmergencyCallsOnly.getId());
}
+ mDateTime.setLayoutParams(clockLp);
}
private void updateBrightnessControllerState() {
@@ -385,4 +396,11 @@
public void setChargingInfo(CharSequence chargingInfo) {
mChargingInfo.setText(chargingInfo);
}
+
+ public void setKeyguardUserSwitcherShowing(boolean showing) {
+ // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
+ mKeyguardUserSwitcherShowing = showing;
+ updateVisibilities();
+ updateSystemIconsLayoutParams();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 09e4d94..a36f3d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -190,19 +190,23 @@
*/
public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
- mPhoneStatusBar.hideKeyguard();
- mStatusBarWindowManager.setKeyguardFadingAway(true);
- mStatusBarWindowManager.setKeyguardShowing(false);
+
long uptimeMillis = SystemClock.uptimeMillis();
long delay = startTime - uptimeMillis;
if (delay < 0) {
delay = 0;
}
+
+ mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration);
+ mPhoneStatusBar.hideKeyguard();
+ mStatusBarWindowManager.setKeyguardFadingAway(true);
+ mStatusBarWindowManager.setKeyguardShowing(false);
mBouncer.animateHide(delay, fadeoutDuration);
mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
@Override
public void run() {
mStatusBarWindowManager.setKeyguardFadingAway(false);
+ mPhoneStatusBar.finishKeyguardFadingAway();
}
});
mViewMediatorCallback.keyguardGone();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
new file mode 100644
index 0000000..6f2642a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -0,0 +1,219 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import com.android.systemui.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * A view that displays a user image cropped to a circle with a frame.
+ */
+public class UserAvatarView extends View {
+
+ private int mActiveFrameColor;
+ private int mFrameColor;
+ private float mFrameWidth;
+ private Bitmap mBitmap;
+ private Drawable mDrawable;
+
+ private final Paint mFramePaint = new Paint();
+ private final Paint mBitmapPaint = new Paint();
+ private final Matrix mDrawMatrix = new Matrix();
+
+ private float mScale = 1;
+
+ public UserAvatarView(Context context, AttributeSet attrs,
+ int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
+ final int N = a.getIndexCount();
+ for (int i = 0; i < N; i++) {
+ int attr = a.getIndex(i);
+ switch (attr) {
+ case R.styleable.UserAvatarView_frameWidth:
+ setFrameWidth(a.getDimension(attr, 0));
+ break;
+ case R.styleable.UserAvatarView_activeFrameColor:
+ setActiveFrameColor(a.getColor(attr, 0));
+ break;
+ case R.styleable.UserAvatarView_frameColor:
+ setFrameColor(a.getColor(attr, 0));
+ break;
+ }
+ }
+ a.recycle();
+
+ mFramePaint.setAntiAlias(true);
+ mFramePaint.setStyle(Paint.Style.STROKE);
+ mBitmapPaint.setAntiAlias(true);
+ }
+
+ public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public UserAvatarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UserAvatarView(Context context) {
+ this(context, null);
+ }
+
+ public void setBitmap(Bitmap bitmap) {
+ setDrawable(null);
+ mBitmap = bitmap;
+ if (mBitmap != null) {
+ mBitmapPaint.setShader(new BitmapShader(
+ bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+ } else {
+ mBitmapPaint.setShader(null);
+ }
+ configureBounds();
+ invalidate();
+ }
+
+ public void setFrameColor(int frameColor) {
+ mFrameColor = frameColor;
+ invalidate();
+ }
+
+ public void setActiveFrameColor(int activeFrameColor) {
+ mActiveFrameColor = activeFrameColor;
+ invalidate();
+ }
+
+ public void setFrameWidth(float frameWidth) {
+ mFrameWidth = frameWidth;
+ invalidate();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ configureBounds();
+ }
+
+ public void configureBounds() {
+ int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
+ int vheight = getHeight() - mPaddingTop - mPaddingBottom;
+
+ int dwidth;
+ int dheight;
+ if (mBitmap != null) {
+ dwidth = mBitmap.getWidth();
+ dheight = mBitmap.getHeight();
+ } else if (mDrawable != null) {
+ dwidth = mDrawable.getIntrinsicWidth();
+ dheight = mDrawable.getIntrinsicHeight();
+ mDrawable.setBounds(0, 0, dwidth, dheight);
+ vwidth -= 2 * (mFrameWidth - 1);
+ vheight -= 2 * (mFrameWidth - 1);
+ } else {
+ return;
+ }
+
+ float scale;
+ float dx;
+ float dy;
+
+ scale = Math.min((float) vwidth / (float) dwidth,
+ (float) vheight / (float) dheight);
+
+ dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
+ dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
+
+ mDrawMatrix.setScale(scale, scale);
+ mDrawMatrix.postTranslate(dx, dy);
+ mScale = scale;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ int frameColor = isActivated() ? mActiveFrameColor : mFrameColor;
+ float halfW = getWidth() / 2f;
+ float halfH = getHeight() / 2f;
+ float halfSW = Math.min(halfH, halfW);
+ if (mBitmap != null && mScale > 0) {
+ int saveCount = canvas.getSaveCount();
+ canvas.save();
+ canvas.translate(mPaddingLeft, mPaddingTop);
+ canvas.concat(mDrawMatrix);
+ float halfBW = mBitmap.getWidth() / 2f;
+ float halfBH = mBitmap.getHeight() / 2f;
+ float halfBSW = Math.min(halfBH, halfBW);
+ canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint);
+ canvas.restoreToCount(saveCount);
+ } else if (mDrawable != null && mScale > 0) {
+ int saveCount = canvas.getSaveCount();
+ canvas.save();
+ canvas.translate(mPaddingLeft, mPaddingTop);
+ canvas.translate(mFrameWidth - 1, mFrameWidth - 1);
+ canvas.concat(mDrawMatrix);
+ mDrawable.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+ if (frameColor != 0) {
+ mFramePaint.setColor(frameColor);
+ mFramePaint.setStrokeWidth(mFrameWidth);
+ canvas.drawCircle(halfW, halfH, halfSW - mFrameWidth / 2f, mFramePaint);
+ }
+ }
+
+ public void setDrawable(Drawable d) {
+ if (mDrawable != null) {
+ mDrawable.setCallback(null);
+ unscheduleDrawable(mDrawable);
+ }
+ mDrawable = d;
+ if (d != null) {
+ d.setCallback(this);
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ d.setLayoutDirection(getLayoutDirection());
+ configureBounds();
+ }
+ if (d != null) {
+ mBitmap = null;
+ }
+ configureBounds();
+ invalidate();
+ }
+
+ @Override
+ public void invalidateDrawable(Drawable dr) {
+ if (dr == mDrawable) {
+ invalidate();
+ } else {
+ super.invalidateDrawable(dr);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
new file mode 100644
index 0000000..c90750c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -0,0 +1,209 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarHeaderView;
+import com.android.systemui.statusbar.phone.UserAvatarView;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.WindowManagerGlobal;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the user switcher on the Keyguard.
+ */
+public class KeyguardUserSwitcher implements View.OnClickListener {
+
+ private static final String TAG = "KeyguardUserSwitcher";
+
+ private final Context mContext;
+ private final ViewGroup mUserSwitcher;
+ private final UserManager mUserManager;
+ private final StatusBarHeaderView mHeader;
+
+ public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
+ StatusBarHeaderView header) {
+ mContext = context;
+ if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher)) {
+ mUserSwitcher = (ViewGroup) userSwitcher.inflate();
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mHeader = header;
+ refresh();
+ } else {
+ mUserSwitcher = null;
+ mUserManager = null;
+ mHeader = null;
+ }
+ }
+
+ public void setKeyguard(boolean keyguard) {
+ if (mUserSwitcher != null) {
+ // TODO: Cache showUserSwitcherOnKeyguard().
+ if (keyguard && showUserSwitcherOnKeyguard()) {
+ show();
+ refresh();
+ } else {
+ hide();
+ }
+ }
+ }
+
+ /**
+ * @return true if the user switcher should be shown on the lock screen.
+ * @see android.os.UserManager#isUserSwitcherEnabled()
+ */
+ private boolean showUserSwitcherOnKeyguard() {
+ // TODO: Set isEdu. The edu provisioning process can add settings to Settings.Global.
+ boolean isEdu = false;
+ if (isEdu) {
+ return true;
+ }
+ List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
+ int N = users.size();
+ int switchableUsers = 0;
+ for (int i = 0; i < N; i++) {
+ if (users.get(i).supportsSwitchTo()) {
+ switchableUsers++;
+ }
+ }
+ return switchableUsers > 1;
+ }
+
+ public void show() {
+ if (mUserSwitcher != null) {
+ // TODO: animate
+ mUserSwitcher.setVisibility(View.VISIBLE);
+ mHeader.setKeyguardUserSwitcherShowing(true);
+ }
+ }
+
+ private void hide() {
+ if (mUserSwitcher != null) {
+ // TODO: animate
+ mUserSwitcher.setVisibility(View.GONE);
+ mHeader.setKeyguardUserSwitcherShowing(false);
+ }
+ }
+
+ private void refresh() {
+ if (mUserSwitcher != null) {
+ new AsyncTask<Void, Void, ArrayList<UserData>>() {
+ @Override
+ protected ArrayList<UserData> doInBackground(Void... params) {
+ return loadUsers();
+ }
+
+ @Override
+ protected void onPostExecute(ArrayList<UserData> userInfos) {
+ bind(userInfos);
+ }
+ }.execute((Void[]) null);
+ }
+ }
+
+ private void bind(ArrayList<UserData> userList) {
+ mUserSwitcher.removeAllViews();
+ int N = userList.size();
+ for (int i = 0; i < N; i++) {
+ mUserSwitcher.addView(inflateUser(userList.get(i)));
+ }
+ // TODO: add Guest
+ // TODO: add (+) button
+ }
+
+ private View inflateUser(UserData user) {
+ View v = LayoutInflater.from(mUserSwitcher.getContext()).inflate(
+ R.layout.keyguard_user_switcher_item, mUserSwitcher, false);
+ TextView name = (TextView) v.findViewById(R.id.name);
+ UserAvatarView picture = (UserAvatarView) v.findViewById(R.id.picture);
+ name.setText(user.userInfo.name);
+ picture.setActivated(user.isCurrent);
+ if (user.userInfo.isGuest()) {
+ picture.setDrawable(mContext.getResources().getDrawable(R.drawable.ic_account_circle));
+ } else {
+ picture.setBitmap(user.userIcon);
+ }
+ v.setOnClickListener(this);
+ v.setTag(user.userInfo);
+ // TODO: mark which user is current for accessibility.
+ return v;
+ }
+
+ @Override
+ public void onClick(View v) {
+ switchUser(((UserInfo)v.getTag()).id);
+ }
+
+ // TODO: Factor out logic below and share with QS implementation.
+
+ private ArrayList<UserData> loadUsers() {
+ ArrayList<UserInfo> users = (ArrayList<UserInfo>) mUserManager
+ .getUsers(true /* excludeDying */);
+ int N = users.size();
+ ArrayList<UserData> result = new ArrayList<>(N);
+ int currentUser = -1;
+ try {
+ currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couln't get current user.", e);
+ }
+ for (int i = 0; i < N; i++) {
+ UserInfo user = users.get(i);
+ if (user.supportsSwitchTo()) {
+ boolean isCurrent = user.id == currentUser;
+ result.add(new UserData(user, mUserManager.getUserIcon(user.id), isCurrent));
+ }
+ }
+ return result;
+ }
+
+ private void switchUser(int userId) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().lockNow(null);
+ ActivityManagerNative.getDefault().switchUser(userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't switch user.", e);
+ }
+ }
+
+ private static class UserData {
+ final UserInfo userInfo;
+ final Bitmap userIcon;
+ final boolean isCurrent;
+
+ UserData(UserInfo userInfo, Bitmap userIcon, boolean isCurrent) {
+ this.userInfo = userInfo;
+ this.userIcon = userIcon;
+ this.isCurrent = isCurrent;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 6d92b05..fcc951e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -32,6 +32,7 @@
private float mOverScrollTopAmount;
private float mOverScrollBottomAmount;
private int mSpeedBumpIndex = -1;
+ private float mScrimAmount;
public int getScrollY() {
return mScrollY;
@@ -85,6 +86,14 @@
}
}
+ public void setScrimAmount(float scrimAmount) {
+ mScrimAmount = scrimAmount;
+ }
+
+ public float getScrimAmount() {
+ return mScrimAmount;
+ }
+
public float getOverScrollAmount(boolean top) {
return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index ccbaed3..f6e9aef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -541,8 +541,9 @@
if (slidingChild.getVisibility() == GONE) {
continue;
}
- float top = slidingChild.getTranslationY();
- float bottom = top + slidingChild.getActualHeight();
+ float childTop = slidingChild.getTranslationY();
+ float top = childTop + slidingChild.getClipTopAmount();
+ float bottom = childTop + slidingChild.getActualHeight();
int left = slidingChild.getLeft();
int right = slidingChild.getRight();
@@ -1845,6 +1846,11 @@
return true;
}
+ public void setScrimAlpha(float progress) {
+ mAmbientState.setScrimAmount(progress);
+ requestChildrenUpdate();
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 602c22b..9a4b798 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -154,6 +154,17 @@
handleDraggedViews(ambientState, resultState, algorithmState);
updateDimmedActivated(ambientState, resultState, algorithmState);
updateClipping(resultState, algorithmState);
+ updateScrimAmount(resultState, algorithmState, ambientState.getScrimAmount());
+ }
+
+ private void updateScrimAmount(StackScrollState resultState,
+ StackScrollAlgorithmState algorithmState, float scrimAmount) {
+ int childCount = algorithmState.visibleChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ View child = algorithmState.visibleChildren.get(i);
+ StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
+ childViewState.scrimAmount = scrimAmount;
+ }
}
private void updateClipping(StackScrollState resultState,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 1ad4acc..02f2cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -148,6 +148,9 @@
// apply dimming
child.setDimmed(state.dimmed, false /* animate */);
+ // apply scrimming
+ child.setScrimAmount(state.scrimAmount);
+
float oldClipTopAmount = child.getClipTopAmount();
if (oldClipTopAmount != state.clipTopAmount) {
child.setClipTopAmount(state.clipTopAmount);
@@ -223,6 +226,12 @@
boolean dimmed;
/**
+ * A value between 0 and 1 indicating how much the view should be scrimmed.
+ * 1 means that the notifications will be darkened as much as possible.
+ */
+ float scrimAmount;
+
+ /**
* The amount which the view should be clipped from the top. This is calculated to
* perceive consistent shadows.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 225398a..0006dad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -176,6 +176,9 @@
// start dimmed animation
child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
+ // apply scrimming
+ child.setScrimAmount(viewState.scrimAmount);
+
if (wasAdded) {
child.performAddAnimation(delay);
}
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/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9d92421..1bd837b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -89,7 +89,7 @@
@Override
public String toString() {
- return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid +
+ return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid + " subId=" + subId +
" events=" + Integer.toHexString(events) + "}";
}
}
@@ -208,11 +208,13 @@
String action = intent.getAction();
Slog.d(TAG, "mBroadcastReceiver: action=" + action);
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED,
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ if (DBG) Slog.d(TAG, "onReceive: userHandle=" + userHandle);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0));
} else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) {
mDefaultSubId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.getDefaultSubId());
+ if (DBG) Slog.d(TAG, "onReceive: mDefaultSubId=" + mDefaultSubId);
mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_DEFAULT_SUB, 0, 0));
}
}
@@ -340,18 +342,19 @@
// the received subId value update the isLegacyApp field
if ((r.subId <= 0) || (r.subId == SubscriptionManager.INVALID_SUB_ID)) {
r.subId = mDefaultSubId;
- r.isLegacyApp = true; // FIXME: is this needed ??
+ r.isLegacyApp = true; // r.subId is to be update when default changes.
}
if (r.subId == SubscriptionManager.DEFAULT_SUB_ID) {
r.subId = mDefaultSubId;
+ r.isLegacyApp = true; // r.subId is to be update when default changes.
if (DBG) Slog.i(TAG, "listen: DEFAULT_SUB_ID");
}
mRecords.add(r);
- if (DBG) Slog.i(TAG, "listen: add new record=" + r);
+ if (DBG) Slog.i(TAG, "listen: add new record");
}
int phoneId = SubscriptionManager.getPhoneId(subId);
- int send = events & (events ^ r.events);
r.events = events;
+ if (DBG) Slog.i(TAG, "listen: set events record=" + r);
if (notifyNow && validatePhoneId(phoneId)) {
if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
try {
@@ -1063,6 +1066,7 @@
pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
pw.println(" mDataConnectionNetworkCapabilities=" +
mDataConnectionNetworkCapabilities);
+ pw.println(" mDefaultSubId=" + mDefaultSubId);
pw.println(" mCellLocation=" + mCellLocation);
pw.println(" mCellInfo=" + mCellInfo);
pw.println(" mDcRtInfo=" + mDcRtInfo);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 816e022..69262c8 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1328,7 +1328,7 @@
+ " app=" + app);
if (app != null && app.thread != null) {
try {
- app.addPackage(r.appInfo.packageName, mAm.mProcessStats);
+ app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
} catch (RemoteException e) {
@@ -1883,7 +1883,8 @@
mPendingServices.remove(i);
i--;
- proc.addPackage(sr.appInfo.packageName, mAm.mProcessStats);
+ proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
+ mAm.mProcessStats);
realStartServiceLocked(sr, proc, sr.createdFromFg);
didSomething = true;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae00e48..40251b9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -570,6 +570,12 @@
long mLastFullPssTime = SystemClock.uptimeMillis();
/**
+ * If set, the next time we collect PSS data we should do a full collection
+ * with data from native processes and the kernel.
+ */
+ boolean mFullPssPending = false;
+
+ /**
* This is the process holding what we currently consider to be
* the "home" activity.
*/
@@ -1801,8 +1807,49 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case COLLECT_PSS_BG_MSG: {
- int i=0, num=0;
long start = SystemClock.uptimeMillis();
+ MemInfoReader memInfo = null;
+ synchronized (ActivityManagerService.this) {
+ if (mFullPssPending) {
+ mFullPssPending = false;
+ memInfo = new MemInfoReader();
+ }
+ }
+ if (memInfo != null) {
+ updateCpuStatsNow();
+ long nativeTotalPss = 0;
+ synchronized (mProcessCpuThread) {
+ final int N = mProcessCpuTracker.countStats();
+ for (int j=0; j<N; j++) {
+ ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(j);
+ if (st.vsize <= 0 || st.uid >= Process.FIRST_APPLICATION_UID
+ || st.uid == Process.SYSTEM_UID) {
+ // This is definitely an application process; skip it.
+ continue;
+ }
+ synchronized (mPidsSelfLocked) {
+ if (mPidsSelfLocked.indexOfKey(st.pid) >= 0) {
+ // This is one of our own processes; skip it.
+ continue;
+ }
+ }
+ nativeTotalPss += Debug.getPss(st.pid, null);
+ }
+ }
+ memInfo.readMemInfo();
+ synchronized (this) {
+ if (DEBUG_PSS) Slog.d(TAG, "Collected native and kernel memory in "
+ + (SystemClock.uptimeMillis()-start) + "ms");
+ mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
+ memInfo.getFreeSizeKb(),
+ memInfo.getSwapTotalSizeKb()-memInfo.getSwapFreeSizeKb(),
+ memInfo.getBuffersSizeKb()+memInfo.getShmemSizeKb()
+ +memInfo.getSlabSizeKb(),
+ nativeTotalPss);
+ }
+ }
+
+ int i=0, num=0;
long[] tmp = new long[1];
do {
ProcessRecord proc;
@@ -2769,7 +2816,7 @@
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
// If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName, mProcessStats);
+ app.addPackage(info.packageName, info.versionCode, mProcessStats);
return app;
}
@@ -2824,7 +2871,7 @@
}
} else {
// If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName, mProcessStats);
+ app.addPackage(info.packageName, info.versionCode, mProcessStats);
}
// If the system is not ready yet, then hold off on starting this
@@ -7648,14 +7695,24 @@
}
}
- private boolean isLockTaskAuthorized(ComponentName name) {
+ private boolean isLockTaskAuthorized(String pkg) {
final DevicePolicyManager dpm = (DevicePolicyManager)
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- return dpm != null && dpm.isLockTaskPermitted(name);
+ try {
+ int uid = mContext.getPackageManager().getPackageUid(pkg,
+ Binder.getCallingUserHandle().getIdentifier());
+ return (uid == Binder.getCallingUid()) && dpm != null && dpm.isLockTaskPermitted(pkg);
+ } catch (NameNotFoundException e) {
+ return false;
+ }
}
private void startLockTaskMode(TaskRecord task) {
- if (!isLockTaskAuthorized(task.intent.getComponent())) {
+ final String pkg;
+ synchronized (this) {
+ pkg = task.intent.getComponent().getPackageName();
+ }
+ if (!isLockTaskAuthorized(pkg)) {
return;
}
long ident = Binder.clearCallingIdentity();
@@ -7664,6 +7721,9 @@
// Since we lost lock on task, make sure it is still there.
task = mStackSupervisor.anyTaskForIdLocked(task.taskId);
if (task != null) {
+ if ((mFocusedActivity == null) || (task != mFocusedActivity.task)) {
+ throw new IllegalArgumentException("Invalid task, not in foreground");
+ }
mStackSupervisor.setLockTaskModeLocked(task);
}
}
@@ -7674,25 +7734,25 @@
@Override
public void startLockTaskMode(int taskId) {
+ final TaskRecord task;
long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task;
synchronized (this) {
task = mStackSupervisor.anyTaskForIdLocked(taskId);
}
- if (task != null) {
- startLockTaskMode(task);
- }
} finally {
Binder.restoreCallingIdentity(ident);
}
+ if (task != null) {
+ startLockTaskMode(task);
+ }
}
@Override
public void startLockTaskMode(IBinder token) {
+ final TaskRecord task;
long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task;
synchronized (this) {
final ActivityRecord r = ActivityRecord.forToken(token);
if (r == null) {
@@ -7700,24 +7760,27 @@
}
task = r.task;
}
- if (task != null) {
- startLockTaskMode(task);
- }
} finally {
Binder.restoreCallingIdentity(ident);
}
+ if (task != null) {
+ startLockTaskMode(task);
+ }
}
@Override
public void stopLockTaskMode() {
- // Check if the calling task is eligible to use lock task
- final int uid = Binder.getCallingUid();
+ // Verify that the user matches the package of the intent for the TaskRecord
+ // we are locked to. This will ensure the same caller for startLockTaskMode and
+ // stopLockTaskMode.
try {
- final String name = AppGlobals.getPackageManager().getNameForUid(uid);
- if (!isLockTaskAuthorized(new ComponentName(name, name))) {
- return;
+ String pkg = mStackSupervisor.mLockTaskModeTask.intent.getPackage();
+ int uid = mContext.getPackageManager().getPackageUid(pkg,
+ Binder.getCallingUserHandle().getIdentifier());
+ if (uid != Binder.getCallingUid()) {
+ throw new SecurityException("Invalid uid, expected " + uid);
}
- } catch (RemoteException e) {
+ } catch (NameNotFoundException e) {
Log.d(TAG, "stopLockTaskMode " + e);
return;
}
@@ -7782,7 +7845,8 @@
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
- app.addPackage(cpi.applicationInfo.packageName, mProcessStats);
+ app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
+ mProcessStats);
}
ensurePackageDexOpt(cpi.applicationInfo.packageName);
}
@@ -12590,6 +12654,8 @@
}
}
+ long nativeProcTotalPss = 0;
+
if (!isCheckinRequest && procs.size() > 1) {
// If we are showing aggregations, also look for native processes to
// include so that our aggregations are more accurate.
@@ -12611,6 +12677,7 @@
final long myTotalPss = mi.getTotalPss();
totalPss += myTotalPss;
+ nativeProcTotalPss += myTotalPss;
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
st.name, myTotalPss, st.pid, false);
@@ -12678,6 +12745,15 @@
}
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
+ if (nativeProcTotalPss > 0) {
+ synchronized (this) {
+ mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
+ memInfo.getFreeSizeKb(),
+ memInfo.getSwapTotalSizeKb()-memInfo.getSwapFreeSizeKb(),
+ memInfo.getBuffersSizeKb()+memInfo.getShmemSizeKb()+memInfo.getSlabSizeKb(),
+ nativeProcTotalPss);
+ }
+ }
if (!brief) {
if (!isCompact) {
pw.print("Total RAM: "); pw.print(memInfo.getTotalSizeKb());
@@ -15458,6 +15534,7 @@
}
if (DEBUG_PSS) Slog.d(TAG, "Requesting PSS of all procs! memLowered=" + memLowered);
mLastFullPssTime = now;
+ mFullPssPending = true;
mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
mPendingPssProcesses.clear();
for (int i=mLruProcesses.size()-1; i>=0; i--) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 30446c7..0825f2e 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -567,7 +567,10 @@
}
boolean isPersistable() {
- return (info.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+ return (info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY ||
+ info.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS) &&
+ (intent == null ||
+ (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0);
}
void makeFinishing() {
@@ -579,11 +582,6 @@
}
}
- boolean isRootActivity() {
- final ArrayList<ActivityRecord> activities = task.mActivities;
- return activities.size() == 0 || this == activities.get(0);
- }
-
UriPermissionOwner getUriPermissionsLocked() {
if (uriPermissions == null) {
uriPermissions = new UriPermissionOwner(service, this);
@@ -1032,11 +1030,11 @@
return -1;
}
final TaskRecord task = r.task;
- switch (task.mActivities.indexOf(r)) {
- case -1: return -1;
- case 0: return task.taskId;
- default: return onlyRoot ? -1 : task.taskId;
+ final int activityNdx = task.mActivities.indexOf(r);
+ if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) {
+ return -1;
}
+ return task.taskId;
}
static ActivityRecord isInStackLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 03ce530..fe2a473 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1066,40 +1066,6 @@
}
}
- /**
- * Determine if home should be visible below the passed record.
- * @param record activity we are querying for.
- * @return true if home is visible below the passed activity, false otherwise.
- */
- boolean isActivityOverHome(ActivityRecord record) {
- // Start at record and go down, look for either home or a visible fullscreen activity.
- final TaskRecord recordTask = record.task;
- for (int taskNdx = mTaskHistory.indexOf(recordTask); taskNdx >= 0; --taskNdx) {
- TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- final int startNdx =
- task == recordTask ? activities.indexOf(record) : activities.size() - 1;
- for (int activityNdx = startNdx; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (r.isHomeActivity()) {
- return true;
- }
- if (!r.finishing && r.fullscreen) {
- // Passed activity is over a fullscreen activity.
- return false;
- }
- }
- if (task.mOnTopOfHome) {
- // Got to the bottom of a task on top of home without finding a visible fullscreen
- // activity. Home is visible.
- return true;
- }
- }
- // Got to the bottom of this stack and still don't know. If this is over the home stack
- // then record is over home. May not work if we ever get more than two layers.
- return mStackSupervisor.isFrontStack(this);
- }
-
private void setVisibile(ActivityRecord r, boolean visible) {
r.visible = visible;
mWindowManager.setAppVisibility(r.appToken, visible);
@@ -1954,8 +1920,7 @@
// existing activities from other tasks in to it.
// If the caller has requested that the target task be
// reset, then do so.
- if ((r.intent.getFlags()
- & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
resetTaskIfNeededLocked(r, r);
doShow = topRunningNonDelayedActivityLocked(null) == r;
}
@@ -2048,7 +2013,8 @@
// the root, we may no longer have the task!).
final ArrayList<ActivityRecord> activities = task.mActivities;
final int numActivities = activities.size();
- for (int i = numActivities - 1; i > 0; --i ) {
+ final int rootActivityNdx = task.findEffectiveRootIndex();
+ for (int i = numActivities - 1; i > rootActivityNdx; --i ) {
ActivityRecord target = activities.get(i);
final int flags = target.info.flags;
@@ -2207,8 +2173,10 @@
final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
final int numActivities = activities.size();
- // Do not operate on the root Activity.
- for (int i = numActivities - 1; i > 0; --i) {
+ final int rootActivityNdx = affinityTask.findEffectiveRootIndex();
+
+ // Do not operate on or below the effective root Activity.
+ for (int i = numActivities - 1; i > rootActivityNdx; --i) {
ActivityRecord target = activities.get(i);
final int flags = target.info.flags;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ed7e594..9763523 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.
@@ -251,7 +254,7 @@
/** If non-null then the task specified remains in front and no other tasks may be started
* until the task exits or #stopLockTaskMode() is called. */
- private TaskRecord mLockTaskModeTask;
+ TaskRecord mLockTaskModeTask;
public ActivityStackSupervisor(ActivityManagerService service) {
mService = service;
@@ -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;
@@ -1162,7 +1178,8 @@
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
- app.addPackage(r.info.packageName, mService.mProcessStats);
+ app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
+ mService.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
@@ -2989,12 +3006,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 +3024,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 +3136,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 +3153,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/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 7b2b04d..cdcc74b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -887,7 +887,8 @@
info.activityInfo.applicationInfo.uid, false);
if (app != null && app.thread != null) {
try {
- app.addPackage(info.activityInfo.packageName, mService.mProcessStats);
+ app.addPackage(info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 8d7d300..a5b80bc 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -53,8 +53,8 @@
final int userId; // user of process.
final String processName; // name of the process
// List of packages running in the process
- final ArrayMap<String, ProcessStats.ProcessState> pkgList
- = new ArrayMap<String, ProcessStats.ProcessState>();
+ final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList
+ = new ArrayMap<String, ProcessStats.ProcessStateHolder>();
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
@@ -371,7 +371,7 @@
uid = _uid;
userId = UserHandle.getUserId(_uid);
processName = _processName;
- pkgList.put(_info.packageName, null);
+ pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.versionCode));
maxAdj = ProcessList.UNKNOWN_ADJ;
curRawAdj = setRawAdj = -100;
curAdj = setAdj = -100;
@@ -398,16 +398,15 @@
info.versionCode, processName);
baseProcessTracker.makeActive();
for (int i=0; i<pkgList.size(); i++) {
- ProcessStats.ProcessState ps = pkgList.valueAt(i);
- if (ps != null && ps != origBase) {
- ps.makeInactive();
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != origBase) {
+ holder.state.makeInactive();
}
- ps = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid,
+ holder.state = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid,
info.versionCode, processName);
- if (ps != baseProcessTracker) {
- ps.makeActive();
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
}
- pkgList.setValueAt(i, ps);
}
}
thread = _thread;
@@ -424,11 +423,11 @@
}
baseProcessTracker = null;
for (int i=0; i<pkgList.size(); i++) {
- ProcessStats.ProcessState ps = pkgList.valueAt(i);
- if (ps != null && ps != origBase) {
- ps.makeInactive();
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != origBase) {
+ holder.state.makeInactive();
}
- pkgList.setValueAt(i, null);
+ holder.state = null;
}
}
}
@@ -572,14 +571,16 @@
/*
* Return true if package has been added false if not
*/
- public boolean addPackage(String pkg, ProcessStatsService tracker) {
+ public boolean addPackage(String pkg, int versionCode, ProcessStatsService tracker) {
if (!pkgList.containsKey(pkg)) {
if (baseProcessTracker != null) {
- ProcessStats.ProcessState state = tracker.getProcessStateLocked(
- pkg, info.uid, info.versionCode, processName);
- pkgList.put(pkg, state);
- if (state != baseProcessTracker) {
- state.makeActive();
+ ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+ versionCode);
+ holder.state = tracker.getProcessStateLocked(
+ pkg, info.uid, versionCode, processName);
+ pkgList.put(pkg, holder);
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
}
} else {
pkgList.put(pkg, null);
@@ -615,23 +616,26 @@
tracker.getMemFactorLocked(), now, pkgList);
if (N != 1) {
for (int i=0; i<N; i++) {
- ProcessStats.ProcessState ps = pkgList.valueAt(i);
- if (ps != null && ps != baseProcessTracker) {
- ps.makeInactive();
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != baseProcessTracker) {
+ holder.state.makeInactive();
}
}
pkgList.clear();
ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
info.packageName, info.uid, info.versionCode, processName);
- pkgList.put(info.packageName, ps);
+ ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+ info.versionCode);
+ holder.state = ps;
+ pkgList.put(info.packageName, holder);
if (ps != baseProcessTracker) {
ps.makeActive();
}
}
} else if (N != 1) {
pkgList.clear();
- pkgList.put(info.packageName, null);
+ pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.versionCode));
}
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 14f3ef9..7ec14c29 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -171,10 +171,17 @@
return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
}
+ public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
+ long nativeMem) {
+ mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
+ }
+
public boolean shouldWriteNowLocked(long now) {
if (now > (mLastWriteTime+WRITE_PERIOD)) {
if (SystemClock.elapsedRealtime()
- > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) {
+ > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) &&
+ SystemClock.uptimeMillis()
+ > (mProcessStats.mTimePeriodStartUptime+ProcessStats.COMMIT_UPTIME_PERIOD)) {
mCommitPending = true;
}
return true;
@@ -212,6 +219,7 @@
if (mPendingWrite == null || !mPendingWriteCommitted) {
mPendingWrite = Parcel.obtain();
mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ mProcessStats.mTimePeriodEndUptime = now;
if (commit) {
mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
}
@@ -439,8 +447,10 @@
mWriteLock.lock();
try {
synchronized (mAm) {
+ long now = SystemClock.uptimeMillis();
mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
- mProcessStats.writeToParcel(current, 0);
+ mProcessStats.mTimePeriodEndUptime = now;
+ mProcessStats.writeToParcel(current, now, 0);
}
if (historic != null) {
ArrayList<String> files = getCommittedFiles(0, false, true);
@@ -470,8 +480,10 @@
Parcel current = Parcel.obtain();
long curTime;
synchronized (mAm) {
+ long now = SystemClock.uptimeMillis();
mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
- mProcessStats.writeToParcel(current, 0);
+ mProcessStats.mTimePeriodEndUptime = now;
+ mProcessStats.writeToParcel(current, now, 0);
curTime = mProcessStats.mTimePeriodEndRealtime
- mProcessStats.mTimePeriodStartRealtime;
}
@@ -568,8 +580,8 @@
static private void dumpHelp(PrintWriter pw) {
pw.println("Process stats (procstats) dump options:");
pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
- pw.println(" [--details] [--full-details] [--current] [--hours] [--active]");
- pw.println(" [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
+ pw.println(" [--details] [--full-details] [--current] [--hours N] [--last N]");
+ pw.println(" [--active] [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
pw.println(" --checkin: perform a checkin: print and delete old committed states.");
pw.println(" --c: print only state in checkin format.");
pw.println(" --csv: output data suitable for putting in a spreadsheet.");
@@ -581,6 +593,7 @@
pw.println(" --full-details: dump all timing and active state details.");
pw.println(" --current: only dump current state.");
pw.println(" --hours: aggregate over about N last hours.");
+ pw.println(" --last: only show the last committed stats at index N (starting at 1).");
pw.println(" --active: only show currently active processes/services.");
pw.println(" --commit: commit current stats to disk and reset to start new stats.");
pw.println(" --reset: reset current stats, without committing.");
@@ -621,6 +634,7 @@
boolean dumpFullDetails = false;
boolean dumpAll = false;
int aggregateHours = 0;
+ int lastIndex = 0;
boolean activeOnly = false;
String reqPackage = null;
boolean csvSepScreenStats = false;
@@ -705,6 +719,20 @@
dumpHelp(pw);
return;
}
+ } else if ("--last".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Error: argument required for --last");
+ dumpHelp(pw);
+ return;
+ }
+ try {
+ lastIndex = Integer.parseInt(args[i]);
+ } catch (NumberFormatException e) {
+ pw.println("Error: --last argument not an int -- " + args[i]);
+ dumpHelp(pw);
+ return;
+ }
} else if ("--active".equals(arg)) {
activeOnly = true;
currentOnly = true;
@@ -818,6 +846,43 @@
dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
dumpDetails, dumpFullDetails, dumpAll, activeOnly);
return;
+ } else if (lastIndex > 0) {
+ pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
+ ArrayList<String> files = getCommittedFiles(0, false, true);
+ if (lastIndex >= files.size()) {
+ pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
+ return;
+ }
+ AtomicFile file = new AtomicFile(new File(files.get(lastIndex)));
+ ProcessStats processStats = new ProcessStats(false);
+ readLocked(processStats, file);
+ if (processStats.mReadError != null) {
+ if (isCheckin || isCompact) pw.print("err,");
+ pw.print("Failure reading "); pw.print(files.get(lastIndex));
+ pw.print("; "); pw.println(processStats.mReadError);
+ return;
+ }
+ String fileStr = file.getBaseFile().getPath();
+ boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
+ if (isCheckin || isCompact) {
+ // Don't really need to lock because we uniquely own this object.
+ processStats.dumpCheckinLocked(pw, reqPackage);
+ } else {
+ pw.print("COMMITTED STATS FROM ");
+ pw.print(processStats.mTimePeriodStartClockStr);
+ if (checkedIn) pw.print(" (checked in)");
+ pw.println(":");
+ if (dumpDetails || dumpFullDetails) {
+ processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
+ activeOnly);
+ if (dumpAll) {
+ pw.print(" mFile="); pw.println(mFile.getBaseFile());
+ }
+ } else {
+ processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
+ }
+ }
+ return;
}
boolean sepNeeded = false;
@@ -859,7 +924,7 @@
// Always dump summary here, dumping all details is just too
// much crud.
if (dumpFullDetails) {
- mProcessStats.dumpLocked(pw, reqPackage, now, false, false,
+ processStats.dumpLocked(pw, reqPackage, now, false, false,
activeOnly);
} else {
processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index bb289fa..c79b33d 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -45,7 +45,7 @@
static final boolean DEBUG = false;
/** When in slow mode don't write tasks out faster than this */
- private static final long INTER_TASK_DELAY_MS = 60000;
+ private static final long INTER_TASK_DELAY_MS = 10000;
private static final long DEBUG_INTER_TASK_DELAY_MS = 5000;
private static final String RECENTS_FILENAME = "_task";
@@ -69,6 +69,7 @@
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
sTasksDir = new File(systemDir, TASKS_DIRNAME);
if (!sTasksDir.exists()) {
+ if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
if (!sTasksDir.mkdir()) {
Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
}
@@ -76,6 +77,7 @@
sImagesDir = new File(systemDir, IMAGES_DIRNAME);
if (!sImagesDir.exists()) {
+ if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
if (!sImagesDir.mkdir()) {
Slog.e(TAG, "Failure creating images directory " + sImagesDir);
}
@@ -172,14 +174,15 @@
TaskRecord.restoreFromXml(in, mStackSupervisor);
if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task);
if (task != null) {
+ task.isPersistable = true;
tasks.add(task);
final int taskId = task.taskId;
recoveredTaskIds.add(taskId);
mStackSupervisor.setNextTaskId(taskId);
}
} else {
- Slog.e(TAG, "restoreTasksLocked Unknown xml event=" + event + " name="
- + name);
+ Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event +
+ " name=" + name);
}
}
XmlUtils.skipCurrentTag(in);
@@ -195,6 +198,7 @@
}
}
if (!DEBUG && deleteFile) {
+ if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
taskFile.delete();
}
}
@@ -209,7 +213,7 @@
Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
@Override
public int compare(TaskRecord lhs, TaskRecord rhs) {
- final long diff = lhs.mLastTimeMoved - rhs.mLastTimeMoved;
+ final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
if (diff < 0) {
return -1;
} else if (diff > 0) {
@@ -233,8 +237,7 @@
try {
taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
} catch (Exception e) {
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Can't parse file=" +
- file.getName());
+ Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
file.delete();
continue;
}
@@ -288,15 +291,18 @@
synchronized(mService) {
final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
persistentTaskIds.clear();
+ if (DEBUG) Slog.d(TAG, "mRecents=" + tasks);
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
task = tasks.get(taskNdx);
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" +
task.isPersistable + " needsPersisting=" + task.needsPersisting);
- if (task.isPersistable) {
+ if (task.isPersistable && !task.stack.isHomeStack()) {
+ if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
persistentTaskIds.add(task.taskId);
if (task.needsPersisting) {
try {
+ if (DEBUG) Slog.d(TAG, "Saving task=" + task);
stringWriter = saveToXml(task);
break;
} catch (IOException e) {
@@ -305,6 +311,8 @@
task.needsPersisting = false;
}
}
+ } else {
+ if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task);
}
}
}
@@ -330,6 +338,8 @@
// Made it through the entire list and didn't find anything new that needed
// persisting.
if (!DEBUG) {
+ if (DEBUG) Slog.d(TAG, "Calling removeObsoleteFiles persistentTaskIds=" +
+ persistentTaskIds);
removeObsoleteFiles(persistentTaskIds);
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 81a0b36..9fff329 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -27,7 +27,6 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.util.Slog;
@@ -57,11 +56,12 @@
private static final String ATTR_ONTOPOFHOME = "on_top_of_home";
private static final String ATTR_LASTDESCRIPTION = "last_description";
private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
+ private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
final int taskId; // Unique identifier for this task.
- final String affinity; // The affinity name for this task, or null.
+ String affinity; // The affinity name for this task, or null.
final IVoiceInteractionSession voiceSession; // Voice interaction session driving task
final IVoiceInteractor voiceInteractor; // Associated interactor to provide to app
Intent intent; // The original intent that started the task.
@@ -111,13 +111,15 @@
* Display.DEFAULT_DISPLAY. */
boolean mOnTopOfHome = false;
+ /** If original intent did not allow relinquishing task identity, save that information */
+ boolean mNeverRelinquishIdentity = true;
+
final ActivityManagerService mService;
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
mService = service;
taskId = _taskId;
- affinity = info.taskAffinity;
voiceSession = _voiceSession;
voiceInteractor = _voiceInteractor;
setIntent(_intent, info);
@@ -128,7 +130,7 @@
String _affinity, ComponentName _realActivity, ComponentName _origActivity,
boolean _rootWasReset, boolean _askedCompatMode, int _taskType, boolean _onTopOfHome,
int _userId, String _lastDescription, ArrayList<ActivityRecord> activities,
- long lastTimeMoved) {
+ long lastTimeMoved, boolean neverRelinquishIdentity) {
mService = service;
taskId = _taskId;
intent = _intent;
@@ -146,6 +148,7 @@
lastDescription = _lastDescription;
mActivities = activities;
mLastTimeMoved = lastTimeMoved;
+ mNeverRelinquishIdentity = neverRelinquishIdentity;
}
void touchActiveTime() {
@@ -157,6 +160,14 @@
}
void setIntent(Intent _intent, ActivityInfo info) {
+ if (intent == null) {
+ mNeverRelinquishIdentity =
+ (info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+ } else if (mNeverRelinquishIdentity) {
+ return;
+ }
+
+ affinity = info.taskAffinity;
stringName = null;
if (info.targetActivity == null) {
@@ -282,6 +293,7 @@
mActivities.remove(newTop);
mActivities.add(newTop);
+ updateEffectiveIntent();
setFrontOfTask();
}
@@ -311,6 +323,7 @@
r.mActivityType = taskType;
}
mActivities.add(index, r);
+ updateEffectiveIntent();
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
}
@@ -322,6 +335,7 @@
// Was previously in list.
numFullscreen--;
}
+ updateEffectiveIntent();
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
}
@@ -579,12 +593,19 @@
// utility activities.
int activityNdx;
final int numActivities = mActivities.size();
+ final boolean relinquish = numActivities == 0 ? false :
+ (mActivities.get(0).info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) != 0;
for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
++activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
+ if (relinquish && (r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
+ // This will be the top activity for determining taskDescription. Pre-inc to
+ // overcome initial decrement below.
+ ++activityNdx;
+ break;
+ }
if (r.intent != null &&
- (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
- != 0) {
+ (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
break;
}
}
@@ -615,6 +636,27 @@
}
}
+ int findEffectiveRootIndex() {
+ int activityNdx;
+ final int topActivityNdx = mActivities.size() - 1;
+ for (activityNdx = 0; activityNdx < topActivityNdx; ++activityNdx) {
+ final ActivityRecord r = mActivities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if ((r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
+ break;
+ }
+ }
+ return activityNdx;
+ }
+
+ void updateEffectiveIntent() {
+ final int effectiveRootIndex = findEffectiveRootIndex();
+ final ActivityRecord r = mActivities.get(effectiveRootIndex);
+ setIntent(r.intent, r.info);
+ }
+
void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
Slog.i(TAG, "Saving task=" + this);
@@ -634,6 +676,7 @@
out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
out.attribute(null, ATTR_ONTOPOFHOME, String.valueOf(mOnTopOfHome));
out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
+ out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
if (lastDescription != null) {
out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
}
@@ -652,8 +695,9 @@
final int numActivities = activities.size();
for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
- if (!r.isPersistable() || (activityNdx > 0 &&
- (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0)) {
+ if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
+ ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
+ activityNdx > 0) {
// Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
break;
}
@@ -683,6 +727,7 @@
int userId = 0;
String lastDescription = null;
long lastTimeOnTop = 0;
+ boolean neverRelinquishIdentity = true;
int taskId = -1;
final int outerDepth = in.getDepth();
@@ -713,6 +758,8 @@
lastDescription = attrValue;
} else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
lastTimeOnTop = Long.valueOf(attrValue);
+ } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
+ neverRelinquishIdentity = Boolean.valueOf(attrValue);
} else {
Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
}
@@ -747,7 +794,7 @@
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, realActivity, origActivity, rootHasReset,
askedCompatMode, taskType, onTopOfHome, userId, lastDescription, activities,
- lastTimeOnTop);
+ lastTimeOnTop, neverRelinquishIdentity);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index a77443d..c7f4f6a 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -119,7 +119,7 @@
public int height;
/**
- * The refresh rate of the display.
+ * The refresh rate of the display, in frames per second.
*/
public float refreshRate;
@@ -144,6 +144,20 @@
public float yDpi;
/**
+ * This is a positive value indicating the phase offset of the VSYNC events provided by
+ * Choreographer relative to the display refresh. For example, if Choreographer reports
+ * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos).
+ */
+ public long appVsyncOffsetNanos;
+
+ /**
+ * This is how far in advance a buffer must be queued for presentation at
+ * a given time. If you want a buffer to appear on the screen at
+ * time N, you must submit the buffer before (N - bufferDeadlineNanos).
+ */
+ public long presentationDeadlineNanos;
+
+ /**
* Display flags.
*/
public int flags;
@@ -219,6 +233,8 @@
&& densityDpi == other.densityDpi
&& xDpi == other.xDpi
&& yDpi == other.yDpi
+ && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+ && presentationDeadlineNanos == other.presentationDeadlineNanos
&& flags == other.flags
&& touch == other.touch
&& rotation == other.rotation
@@ -242,6 +258,8 @@
densityDpi = other.densityDpi;
xDpi = other.xDpi;
yDpi = other.yDpi;
+ appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+ presentationDeadlineNanos = other.presentationDeadlineNanos;
flags = other.flags;
touch = other.touch;
rotation = other.rotation;
@@ -261,6 +279,8 @@
sb.append(", ").append(refreshRate).append(" fps, ");
sb.append("density ").append(densityDpi);
sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
+ sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
+ sb.append(", presDeadline ").append(presentationDeadlineNanos);
sb.append(", touch ").append(touchToString(touch));
sb.append(", rotation ").append(rotation);
sb.append(", type ").append(Display.typeToString(type));
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index e80aecd..098537c 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -161,6 +161,8 @@
mInfo.width = mPhys.width;
mInfo.height = mPhys.height;
mInfo.refreshRate = mPhys.refreshRate;
+ mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
+ mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
mInfo.state = mState;
// Assume that all built-in displays that have secure output (eg. HDCP) also
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index d61a35b..ed619d9 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -213,6 +213,8 @@
mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
+ mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;
+ mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;
mBaseDisplayInfo.state = deviceInfo.state;
mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index bfd8372c..3b23b6a 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -191,6 +191,7 @@
private final int mWidth;
private final int mHeight;
private final float mRefreshRate;
+ private final long mDisplayPresentationDeadlineNanos;
private final int mDensityDpi;
private final boolean mSecure;
@@ -200,7 +201,7 @@
private DisplayDeviceInfo mInfo;
public OverlayDisplayDevice(IBinder displayToken, String name,
- int width, int height, float refreshRate,
+ int width, int height, float refreshRate, long presentationDeadlineNanos,
int densityDpi, boolean secure, int state,
SurfaceTexture surfaceTexture) {
super(OverlayDisplayAdapter.this, displayToken);
@@ -208,6 +209,7 @@
mWidth = width;
mHeight = height;
mRefreshRate = refreshRate;
+ mDisplayPresentationDeadlineNanos = presentationDeadlineNanos;
mDensityDpi = densityDpi;
mSecure = secure;
mState = state;
@@ -249,6 +251,8 @@
mInfo.densityDpi = mDensityDpi;
mInfo.xDpi = mDensityDpi;
mInfo.yDpi = mDensityDpi;
+ mInfo.presentationDeadlineNanos = mDisplayPresentationDeadlineNanos +
+ 1000000000L / (int) mRefreshRate; // display's deadline + 1 frame
mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION;
if (mSecure) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
@@ -297,12 +301,13 @@
// Called on the UI thread.
@Override
- public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate, int state) {
+ public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate,
+ long presentationDeadlineNanos, int state) {
synchronized (getSyncRoot()) {
IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
mDevice = new OverlayDisplayDevice(displayToken, mName,
- mWidth, mHeight, refreshRate, mDensityDpi, mSecure,
- state, surfaceTexture);
+ mWidth, mHeight, refreshRate, presentationDeadlineNanos,
+ mDensityDpi, mSecure, state, surfaceTexture);
sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
index 06891f3..9ca5fda 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
@@ -303,7 +303,7 @@
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
mListener.onWindowCreated(surfaceTexture, mDefaultDisplayInfo.refreshRate,
- mDefaultDisplayInfo.state);
+ mDefaultDisplayInfo.presentationDeadlineNanos, mDefaultDisplayInfo.state);
}
@Override
@@ -373,7 +373,7 @@
*/
public interface Listener {
public void onWindowCreated(SurfaceTexture surfaceTexture,
- float refreshRate, int state);
+ float refreshRate, long presentationDeadlineNanos, int state);
public void onWindowDestroyed();
public void onStateChanged(int state);
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 14ef5a9..ec14cf1 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -171,6 +171,7 @@
mInfo.densityDpi = mDensityDpi;
mInfo.xDpi = mDensityDpi;
mInfo.yDpi = mDensityDpi;
+ mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame
mInfo.flags = 0;
if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index cd57941..a05bf2c 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -127,7 +127,7 @@
pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
-
+
// Try to dump the controller state.
if (mDisplayController == null) {
pw.println("mDisplayController=null");
@@ -729,6 +729,7 @@
mInfo.width = mWidth;
mInfo.height = mHeight;
mInfo.refreshRate = mRefreshRate;
+ mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame
mInfo.flags = mFlags;
mInfo.type = Display.TYPE_WIFI;
mInfo.address = mAddress;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 353f603..52c092c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -78,7 +78,7 @@
*/
void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
assertRunOnServiceThread();
- HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
+ HdmiCecDeviceInfo targetDevice = getDeviceInfo(targetAddress);
if (targetDevice == null) {
invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
return;
@@ -142,6 +142,9 @@
}
private static void invokeCallback(IHdmiControlCallback callback, int result) {
+ if (callback == null) {
+ return;
+ }
try {
callback.onComplete(result);
} catch (RemoteException e) {
@@ -455,8 +458,11 @@
final void addCecDevice(HdmiCecDeviceInfo info) {
assertRunOnServiceThread();
addDeviceInfo(info);
-
- // TODO: announce new device detection.
+ if (info.getLogicalAddress() == mAddress) {
+ // The addition of TV device itself should not be notified.
+ return;
+ }
+ mService.invokeDeviceEventListeners(info, true);
}
/**
@@ -466,10 +472,9 @@
*/
final void removeCecDevice(int address) {
assertRunOnServiceThread();
- removeDeviceInfo(address);
+ HdmiCecDeviceInfo info = removeDeviceInfo(address);
mCecMessageCache.flushMessagesFrom(address);
-
- // TODO: announce a device removal.
+ mService.invokeDeviceEventListeners(info, false);
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fddb833..d323f34 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -37,6 +37,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
@@ -94,21 +95,25 @@
// List of listeners registered by callers that want to get notified of
// hotplug events.
+ @GuardedBy("mLock")
private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
// List of records for hotplug event listener to handle the the caller killed in action.
+ @GuardedBy("mLock")
private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
new ArrayList<>();
// List of listeners registered by callers that want to get notified of
// device status events.
+ @GuardedBy("mLock")
private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
// List of records for device event listener to handle the the caller killed in action.
+ @GuardedBy("mLock")
private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
new ArrayList<>();
- // Handler running on service thread. It's used to run a task in service thread.
+ // Handler used to run a task in service thread.
private final Handler mHandler = new Handler();
@Nullable
@@ -153,6 +158,7 @@
// A container for [Logical Address, Local device info].
final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
final SparseIntArray finished = new SparseIntArray();
+ mCecController.clearLogicalAddress();
for (int type : deviceTypes) {
final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
localDevice.init();
@@ -171,8 +177,7 @@
}
finished.append(deviceType, logicalAddress);
- // Once finish address allocation for all devices, notify
- // it to each device.
+ // Address allocation completed for all devices. Notify each device.
if (deviceTypes.size() == finished.size()) {
notifyAddressAllocated(devices);
}
@@ -185,7 +190,7 @@
for (int i = 0; i < devices.size(); ++i) {
int address = devices.keyAt(i);
HdmiCecLocalDevice device = devices.valueAt(i);
- device.onAddressAllocated(address);
+ device.handleAddressAllocated(address);
}
}
@@ -663,15 +668,28 @@
}
private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
+ DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Listener already died");
+ return;
+ }
synchronized (mLock) {
- for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) {
- if (record.mListener.asBinder() == listener.asBinder()) {
- listener.asBinder().unlinkToDeath(record, 0);
- mDeviceEventListenerRecords.remove(record);
- break;
+ mDeviceEventListeners.add(listener);
+ mDeviceEventListenerRecords.add(record);
+ }
+ }
+
+ void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
+ synchronized (mLock) {
+ for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
+ try {
+ listener.onStatusChanged(device, activated);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report device event:" + e);
}
}
- mHotplugEventListeners.remove(listener);
}
}
@@ -683,16 +701,16 @@
}
}
- private void announceHotplugEvent(int portNo, boolean connected) {
- HdmiHotplugEvent event = new HdmiHotplugEvent(portNo, connected);
+ private void announceHotplugEvent(int portId, boolean connected) {
+ HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
synchronized (mLock) {
for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
- invokeHotplugEventListener(listener, event);
+ invokeHotplugEventListenerLocked(listener, event);
}
}
}
- private void invokeHotplugEventListener(IHdmiHotplugEventListener listener,
+ private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
HdmiHotplugEvent event) {
try {
listener.onReceived(event);
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index e48b0dc..92418ab 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -28,9 +28,6 @@
// State in which waits for <SetSystemAudioMode>.
private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 1;
- // State in which waits for <ReportAudioStatus>.
- private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 2;
-
private static final int MAX_SEND_RETRY_COUNT = 2;
private static final int ON_TIMEOUT_MS = 5000;
@@ -92,39 +89,6 @@
tv().setSystemAudioMode(mode);
}
- protected void sendGiveAudioStatus() {
- HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(),
- mAvrLogicalAddress);
- sendCommand(command, new HdmiControlService.SendMessageCallback() {
- @Override
- public void onSendCompleted(int error) {
- if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
- mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
- addTimer(mState, TIMEOUT_MS);
- } else {
- handleSendGiveAudioStatusFailure();
- }
- }
- });
- }
-
- private void handleSendGiveAudioStatusFailure() {
- // TODO: Notify the failure status.
-
- int uiCommand = tv().getSystemAudioMode()
- ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON
- : HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF
- sendUserControlPressedAndReleased(uiCommand);
- finish();
- }
-
- private void sendUserControlPressedAndReleased(int uiCommand) {
- sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
- getSourceAddress(), mAvrLogicalAddress, uiCommand));
- sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
- getSourceAddress(), mAvrLogicalAddress));
- }
-
@Override
final boolean processCommand(HdmiCecMessage cmd) {
switch (mState) {
@@ -137,7 +101,8 @@
boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
if (receivedStatus == mTargetAudioStatus) {
setSystemAudioMode(receivedStatus);
- sendGiveAudioStatus();
+ startAudioStatusAction();
+ return true;
} else {
// Unexpected response, consider the request is newly initiated by AVR.
// To return 'false' will initiate new SystemAudioActionFromAvr by the control
@@ -145,28 +110,14 @@
finish();
return false;
}
- return true;
-
- case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
- // TODO: Handle <FeatureAbort> of <GiveAudioStatus>
- if (cmd.getOpcode() != HdmiCec.MESSAGE_REPORT_AUDIO_STATUS
- || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
- return false;
- }
- byte[] params = cmd.getParams();
- if (params.length > 0) {
- boolean mute = (params[0] & 0x80) == 0x80;
- int volume = params[0] & 0x7F;
- tv().setAudioStatus(mute, volume);
- if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) {
- // Toggle AVR's mute status to match with the system audio status.
- sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
- }
- }
- finish();
- return true;
+ default:
+ return false;
}
- return false;
+ }
+
+ protected void startAudioStatusAction() {
+ addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress));
+ finish();
}
protected void removeSystemAudioActionInProgress() {
@@ -183,9 +134,6 @@
case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
handleSendSystemAudioModeRequestTimeout();
return;
- case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
- handleSendGiveAudioStatusFailure();
- return;
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index 3907b71..b743c64 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -59,7 +59,7 @@
// TODO: Stop the action for System Audio Mode initialization if it is running.
if (mTargetAudioStatus) {
setSystemAudioMode(true);
- sendGiveAudioStatus();
+ startAudioStatusAction();
} else {
setSystemAudioMode(false);
finish();
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
index 75e4fef..5f4fc23 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -25,7 +25,6 @@
/**
* Action to update audio status (volume or mute) of audio amplifier
*/
-// TODO: refactor SystemAudioMode so that it uses this class instead of internal state.
final class SystemAudioStatusAction extends FeatureAction {
private static final String TAG = "SystemAudioStatusAction";
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/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 9ae8aed..9d61493 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -38,6 +38,7 @@
import android.media.MediaMetadata;
import android.media.Rating;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -63,6 +64,7 @@
*/
public class MediaSessionRecord implements IBinder.DeathRecipient {
private static final String TAG = "MediaSessionRecord";
+ private static final boolean DEBUG = false;
/**
* These are the playback states that count as currently active.
@@ -506,9 +508,12 @@
ISessionControllerCallback cb = mControllerCallbacks.get(i);
try {
cb.onPlaybackStateChanged(mPlaybackState);
- } catch (RemoteException e) {
- Log.w(TAG, "Removing dead callback in pushPlaybackStateUpdate.", e);
+ } catch (DeadObjectException e) {
mControllerCallbacks.remove(i);
+ Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate. size="
+ + mControllerCallbacks.size() + " cb=" + cb, e);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushPlaybackStateUpdate.", e);
}
}
}
@@ -523,9 +528,11 @@
ISessionControllerCallback cb = mControllerCallbacks.get(i);
try {
cb.onMetadataChanged(mMetadata);
- } catch (RemoteException e) {
- Log.w(TAG, "Removing dead callback in pushMetadataUpdate.", e);
+ } catch (DeadObjectException e) {
+ Log.w(TAG, "Removing dead callback in pushMetadataUpdate. " + cb, e);
mControllerCallbacks.remove(i);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushMetadataUpdate. " + cb, e);
}
}
}
@@ -540,9 +547,11 @@
ISessionControllerCallback cb = mControllerCallbacks.get(i);
try {
cb.onRouteChanged(mRoute);
- } catch (RemoteException e) {
+ } catch (DeadObjectException e) {
Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
mControllerCallbacks.remove(i);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushRouteUpdate.", e);
}
}
}
@@ -557,8 +566,11 @@
ISessionControllerCallback cb = mControllerCallbacks.get(i);
try {
cb.onEvent(event, data);
+ } catch (DeadObjectException e) {
+ Log.w(TAG, "Removing dead callback in pushEvent.", e);
+ mControllerCallbacks.remove(i);
} catch (RemoteException e) {
- Log.w(TAG, "Error with callback in pushEvent.", e);
+ Log.w(TAG, "unexpected exception in pushEvent.", e);
}
}
}
@@ -611,6 +623,16 @@
return result == null ? state : result;
}
+ private int getControllerCbIndexForCb(ISessionControllerCallback cb) {
+ IBinder binder = cb.asBinder();
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ if (binder.equals(mControllerCallbacks.get(i).asBinder())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
private final RouteConnectionRecord.Listener mConnectionListener
= new RouteConnectionRecord.Listener() {
@Override
@@ -929,8 +951,11 @@
@Override
public void registerCallbackListener(ISessionControllerCallback cb) {
synchronized (mLock) {
- if (!mControllerCallbacks.contains(cb)) {
+ if (getControllerCbIndexForCb(cb) < 0) {
mControllerCallbacks.add(cb);
+ if (DEBUG) {
+ Log.d(TAG, "registering controller callback " + cb);
+ }
}
}
}
@@ -939,7 +964,13 @@
public void unregisterCallbackListener(ISessionControllerCallback cb)
throws RemoteException {
synchronized (mLock) {
- mControllerCallbacks.remove(cb);
+ int index = getControllerCbIndexForCb(cb);
+ if (index != -1) {
+ mControllerCallbacks.remove(index);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "unregistering callback " + cb + ". index=" + index);
+ }
}
}
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..90e263a 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;
}
@@ -4182,6 +4229,7 @@
try {
pp.collectCertificates(pkg, parseFlags);
+ pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
mLastScanError = e.error;
return false;
@@ -4199,14 +4247,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 +4689,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 +4879,7 @@
}
}
if (p != null) {
- usesLibraryFiles.add(p.codePath);
- if (!ArrayUtils.isEmpty(p.splitCodePaths)) {
- Collections.addAll(usesLibraryFiles, p.splitCodePaths);
- }
+ usesLibraryFiles.addAll(p.getAllCodePaths());
}
}
@@ -5674,7 +5718,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 +9758,7 @@
return PackageManager.INSTALL_SUCCEEDED;
}
- };
+ }
static String getAsecPackageName(String packageCid) {
int idx = packageCid.lastIndexOf("-");
@@ -10206,13 +10251,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;
@@ -10228,6 +10273,7 @@
try {
pp.collectCertificates(pkg, parseFlags);
+ pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.returnCode = e.error;
return;
@@ -11542,17 +11588,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 +11609,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/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b941657..1839259 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -744,7 +744,7 @@
writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_APPS);
+ writeBoolean(serializer, restrictions, UserManager.DISALLOW_APPS_CONTROL);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
@@ -896,7 +896,7 @@
readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS);
readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_APPS);
+ readBoolean(parser, restrictions, UserManager.DISALLOW_APPS_CONTROL);
readBoolean(parser, restrictions,
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
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 e34f42b..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();
@@ -116,6 +123,19 @@
}
}
+ private boolean checkUidChangedLocked(
+ Connection connection, int callingUid, int resolvedUserId) {
+ Integer connectionCallingUid = connection.getCallingUidLocked();
+ Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
+ if (connectionCallingUid == null || connectionResolvedUserId == null) {
+ return true;
+ }
+ if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
+ return true;
+ }
+ return false;
+ }
+
/**
* Create a TvInputHardware object with a specific deviceId. One service at a time can access
* the object, and if more than one process attempts to create hardware with the same deviceId,
@@ -133,8 +153,7 @@
Slog.e(TAG, "Invalid deviceId : " + deviceId);
return null;
}
- if (connection.getCallingUidLocked() != callingUid
- || connection.getResolvedUserIdLocked() != resolvedUserId) {
+ if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked());
try {
callback.asBinder().linkToDeath(connection, 0);
@@ -160,8 +179,7 @@
return;
}
if (connection.getHardwareLocked() != hardware
- || connection.getCallingUidLocked() != callingUid
- || connection.getResolvedUserIdLocked() != resolvedUserId) {
+ || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
return;
}
connection.resetLocked(null, null, null, null);
@@ -226,11 +244,11 @@
return mConfigs;
}
- public int getCallingUidLocked() {
+ public Integer getCallingUidLocked() {
return mCallingUid;
}
- public int getResolvedUserIdLocked() {
+ public Integer getResolvedUserIdLocked() {
return mResolvedUserId;
}
@@ -247,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;
}
}
@@ -277,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;
}
}
@@ -288,7 +358,7 @@
throw new IllegalStateException("Device already released.");
}
}
- // TODO
+ // TODO: Use AudioGain?
}
@Override
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 10a67c4..99b5b03 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -19,12 +19,15 @@
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.OperationApplicationException;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -66,11 +69,12 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/** This class provides a system service that manages television inputs. */
public final class TvInputManagerService extends SystemService {
@@ -123,6 +127,44 @@
buildTvInputListLocked(mCurrentUserId);
}
}
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(mCurrentUserId);
+ if (!userState.packageList.contains(packageName)) {
+ // Not a TV input package.
+ return;
+ }
+ }
+
+ ArrayList<ContentProviderOperation> operations =
+ new ArrayList<ContentProviderOperation>();
+
+ String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?";
+ String[] selectionArgs = { packageName };
+
+ operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI)
+ .withSelection(selection, selectionArgs).build());
+ operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI)
+ .withSelection(selection, selectionArgs).build());
+ operations.add(ContentProviderOperation
+ .newDelete(TvContract.WatchedPrograms.CONTENT_URI)
+ .withSelection(selection, selectionArgs).build());
+
+ ContentProviderResult[] results = null;
+ try {
+ results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+ } catch (RemoteException | OperationApplicationException e) {
+ Slog.e(TAG, "error in applyBatch" + e);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid
+ + ")");
+ Slog.d(TAG, "results=" + results);
+ }
+ }
};
monitor.register(mContext, null, UserHandle.ALL, true);
@@ -145,6 +187,7 @@
private void buildTvInputListLocked(int userId) {
UserState userState = getUserStateLocked(userId);
userState.inputMap.clear();
+ userState.packageList.clear();
if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
@@ -162,6 +205,7 @@
TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
if (DEBUG) Slog.d(TAG, "add " + info.getId());
userState.inputMap.put(info.getId(), info);
+ userState.packageList.add(si.packageName);
} catch (IOException | XmlPullParserException e) {
Slog.e(TAG, "Can't load TV input " + si.name, e);
}
@@ -348,7 +392,7 @@
if (session == null) {
removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
- null, null, sessionState.mSeq, userId);
+ null, null, sessionState.mSeq);
} else {
try {
session.asBinder().linkToDeath(sessionState, 0);
@@ -364,7 +408,7 @@
clientState.mSessionTokens.add(sessionState.mSessionToken);
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
- sessionToken, channels[0], sessionState.mSeq, userId);
+ sessionToken, channels[0], sessionState.mSeq);
}
channels[0].dispose();
}
@@ -449,13 +493,13 @@
Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
- sessionState.mSeq, userId);
+ sessionState.mSeq);
}
channels[1].dispose();
}
private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
- IBinder sessionToken, InputChannel channel, int seq, int userId) {
+ IBinder sessionToken, InputChannel channel, int seq) {
try {
client.onSessionCreated(inputId, sessionToken, channel, seq);
} catch (RemoteException exception) {
@@ -587,7 +631,9 @@
} finally {
Binder.restoreCallingIdentity(identity);
}
- return false;
+ // STOPSHIP: Redesign the API around the availability change. For now, the service
+ // will be always available.
+ return true;
}
@Override
@@ -672,7 +718,7 @@
}
// Send a null token immediately while reconnecting.
if (serviceState.mReconnecting == true) {
- sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
+ sendSessionTokenToClientLocked(client, inputId, null, null, seq);
return;
}
@@ -784,7 +830,10 @@
}
// Create a log entry and fill it later.
+ String packageName = userState.inputMap.get(sessionState.mInputId)
+ .getServiceInfo().packageName;
ContentValues values = new ContentValues();
+ values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
currentTime);
values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0);
@@ -931,6 +980,9 @@
// A mapping from the TV input id to its TvInputInfo.
private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
+ // A list of all TV input packages.
+ private final Set<String> packageList = new HashSet<String>();
+
// A mapping from the token of a client to its state.
private final Map<IBinder, ClientState> clientStateMap =
new HashMap<IBinder, ClientState>();
@@ -1095,8 +1147,7 @@
if (sessionState.mSession == null) {
removeSessionStateLocked(sessionToken, sessionState.mUserId);
sendSessionTokenToClientLocked(sessionState.mClient,
- sessionState.mInputId, null, null, sessionState.mSeq,
- sessionState.mUserId);
+ sessionState.mInputId, null, null, sessionState.mSeq);
}
}
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 4574caf..2801f4f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -195,7 +195,7 @@
= new ArrayList<ActiveAdmin>();
// This is the list of component allowed to start lock task mode.
- final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
+ final List<String> mLockTaskPackages = new ArrayList<String>();
ComponentName mRestrictionsProvider;
@@ -1014,10 +1014,10 @@
out.endTag(null, "active-password");
}
- for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
- ComponentName component = policy.mLockTaskComponents.get(i);
+ for (int i=0; i<policy.mLockTaskPackages.size(); i++) {
+ String component = policy.mLockTaskPackages.get(i);
out.startTag(null, LOCK_TASK_COMPONENTS_XML);
- out.attribute(null, "name", component.flattenToString());
+ out.attribute(null, "name", component);
out.endTag(null, LOCK_TASK_COMPONENTS_XML);
}
@@ -1077,7 +1077,7 @@
type = parser.next();
int outerDepth = parser.getDepth();
- policy.mLockTaskComponents.clear();
+ policy.mLockTaskPackages.clear();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1131,9 +1131,7 @@
parser.getAttributeValue(null, "nonletter"));
XmlUtils.skipCurrentTag(parser);
} else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) {
- policy.mLockTaskComponents.add
- (ComponentName.unflattenFromString
- (parser.getAttributeValue(null, "name")));
+ policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
@@ -1324,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()) {
@@ -2384,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;
@@ -2398,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;
}
@@ -2423,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);
}
}
@@ -3456,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
@@ -3482,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
@@ -3723,38 +3730,40 @@
}
/**
- * Sets which componets may enter lock task mode.
+ * Sets which packages may enter lock task mode.
*
* This function can only be called by the device owner or the profile owner.
* @param components The list of components allowed to enter lock task mode.
*/
- public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+ public void setLockTaskPackages(String[] packages) throws SecurityException {
// Get the package names of the caller.
int uid = Binder.getCallingUid();
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
- // Check whether any of the package name is the device owner or the profile owner.
- for (int i=0; i<packageNames.length; i++) {
- String packageName = packageNames[i];
- int userHandle = UserHandle.getUserId(uid);
- String profileOwnerPackage = getProfileOwner(userHandle);
- if (isDeviceOwner(packageName) ||
- (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
+ synchronized (this) {
+ // Check whether any of the package name is the device owner or the profile owner.
+ for (int i=0; i<packageNames.length; i++) {
+ String packageName = packageNames[i];
+ int userHandle = UserHandle.getUserId(uid);
+ String profileOwnerPackage = getProfileOwner(userHandle);
+ if (isDeviceOwner(packageName) ||
+ (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
- // If a package name is the device owner or the profile owner,
- // we update the component list.
- DevicePolicyData policy = getUserData(userHandle);
- policy.mLockTaskComponents.clear();
- if (components != null) {
- for (int j=0; j<components.length; j++) {
- ComponentName component = components[j];
- policy.mLockTaskComponents.add(component);
+ // If a package name is the device owner or the profile owner,
+ // we update the component list.
+ DevicePolicyData policy = getUserData(userHandle);
+ policy.mLockTaskPackages.clear();
+ if (packages != null) {
+ for (int j = 0; j < packages.length; j++) {
+ String pkg = packages[j];
+ policy.mLockTaskPackages.add(pkg);
+ }
}
- }
- // Store the settings persistently.
- saveSettingsLocked(userHandle);
- return;
+ // Store the settings persistently.
+ saveSettingsLocked(userHandle);
+ return;
+ }
}
}
throw new SecurityException();
@@ -3763,36 +3772,61 @@
/**
* This function returns the list of components allowed to start the task lock mode.
*/
- public ComponentName[] getLockTaskComponents() {
- int userHandle = UserHandle.USER_OWNER;
- DevicePolicyData policy = getUserData(userHandle);
- ComponentName[] tempArray = policy.mLockTaskComponents.toArray(new ComponentName[0]);
- return tempArray;
+ public String[] getLockTaskPackages() {
+ synchronized (this) {
+ int userHandle = UserHandle.USER_OWNER;
+ DevicePolicyData policy = getUserData(userHandle);
+ return policy.mLockTaskPackages.toArray(new String[0]);
+ }
}
/**
- * This function lets the caller know whether the given component is allowed to start the
+ * This function lets the caller know whether the given package is allowed to start the
* lock task mode.
- * @param component The component to check
+ * @param pkg The package to check
*/
- public boolean isLockTaskPermitted(ComponentName component) {
+ public boolean isLockTaskPermitted(String pkg) {
// Get current user's devicepolicy
int uid = Binder.getCallingUid();
int userHandle = UserHandle.getUserId(uid);
DevicePolicyData policy = getUserData(userHandle);
- for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
- ComponentName lockTaskComponent = policy.mLockTaskComponents.get(i);
+ synchronized (this) {
+ for (int i = 0; i < policy.mLockTaskPackages.size(); i++) {
+ String lockTaskPackage = policy.mLockTaskPackages.get(i);
- // If the given component equals one of the component stored our device-owner-set
- // list, we allow this component to start the lock task mode.
- if (lockTaskComponent.getPackageName().equals(component.getPackageName())) {
- return true;
+ // If the given package equals one of the packages stored our list,
+ // we allow this package to start lock task mode.
+ if (lockTaskPackage.equals(pkg)) {
+ return true;
+ }
}
}
return false;
}
@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/SubInfoRecord.java b/telephony/java/android/telephony/SubInfoRecord.java
index 670def7..ced8e2f 100644
--- a/telephony/java/android/telephony/SubInfoRecord.java
+++ b/telephony/java/android/telephony/SubInfoRecord.java
@@ -105,4 +105,11 @@
return 0;
}
+ public String toString() {
+ return "{mSubId=" + mSubId + ", mIccId=" + mIccId + " mSlotId=" + mSlotId
+ + " mDisplayName=" + mDisplayName + " mNameSource=" + mNameSource
+ + " mColor=" + mColor + " mNumber=" + mNumber
+ + " mDispalyNumberFormat=" + mDispalyNumberFormat + " mDataRoaming=" + mDataRoaming
+ + " mSimIconRes=" + mSimIconRes + "}";
+ }
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 859a890..79e9fd5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -697,12 +697,16 @@
public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
long [] subId = SubscriptionManager.getSubId(phoneId);
if ((subId != null) && (subId.length >= 1)) {
- if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
- intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); //FIXME: RENAME TO PHONE_ID_KEY ??
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId[0]);
+ putPhoneIdAndSubIdExtra(intent, phoneId, subId[0]);
} else {
logd("putPhoneIdAndSubIdExtra: no valid subs");
}
}
+
+ public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, long subId) {
+ if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
+ intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); //FIXME: RENAME TO PHONE_ID_KEY ??
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ }
}
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/anim/trim_path_animation03.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
index 72beba2..0c1073e 100644
--- a/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
@@ -21,6 +21,8 @@
android:duration="6000"
android:propertyName="rotation"
android:valueFrom="0"
- android:valueTo="360"/>
+ android:valueTo="360"
+ android:interpolator="@interpolator/custom_path_interpolator"
+ />
</set>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
index ff86668..4d0aae1 100644
--- a/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
@@ -16,11 +16,9 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android" >
-
<objectAnimator
android:duration="9000"
- android:propertyName="rotation"
- android:valueFrom="0"
- android:valueTo="360"/>
-
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="m0,0 q 150, 300 150, 0 t 150, 0, t 150, 0 t -150 0 t -150 0 t -150 0 z" />
</set>
\ No newline at end of file
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/res/drawable/animation_vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
index b8681b6..0900b7c 100644
--- a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
@@ -32,5 +32,8 @@
<target
android:name="rotationGroupBlue"
android:animation="@anim/trim_path_animation03" />
+ <target
+ android:name="rotationGroup"
+ android:animation="@anim/trim_path_animation04" />
</animated-vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml b/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml
new file mode 100644
index 0000000..0cffa0a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml
@@ -0,0 +1,2 @@
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="m0,0q0.4,0.05 0.6,0.3t0.3,0.3l0.1,0.4" />
\ No newline at end of file
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)
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 00e1cd8..e83eed7 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -132,5 +132,13 @@
void enableVerboseLogging(int verbose);
int getVerboseLoggingLevel();
+
+ int getAggressiveHandover();
+
+ void enableAggressiveHandover(int enabled);
+
+ int getAllowScansWithTraffic();
+
+ void setAllowScansWithTraffic(int enabled);
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index b64ad60..59b48e4 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -373,19 +373,19 @@
public static int GOOD_RSSI_24 = -65;
/** @hide **/
- public static int LOW_RSSI_24 = -75;
+ public static int LOW_RSSI_24 = -77;
/** @hide **/
- public static int BAD_RSSI_24 = -85;
+ public static int BAD_RSSI_24 = -87;
/** @hide **/
- public static int GOOD_RSSI_5 = -55;
+ public static int GOOD_RSSI_5 = -60;
/** @hide **/
- public static int LOW_RSSI_5 = -65;
+ public static int LOW_RSSI_5 = -72;
/** @hide **/
- public static int BAD_RSSI_5 = -75;
+ public static int BAD_RSSI_5 = -82;
/** @hide **/
public static int UNWANTED_BLACKLIST_SOFT_BUMP = 4;
@@ -394,7 +394,7 @@
public static int UNWANTED_BLACKLIST_HARD_BUMP = 8;
/** @hide **/
- public static int UNBLACKLIST_THRESHOLD_24_SOFT = -75;
+ public static int UNBLACKLIST_THRESHOLD_24_SOFT = -77;
/** @hide **/
public static int UNBLACKLIST_THRESHOLD_24_HARD = -68;
@@ -415,6 +415,19 @@
* 5GHz band is prefered over 2.4 if the 5GHz RSSI is higher than this threshold **/
public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65;
+ /** @hide
+ * 5GHz band is penalized if the 5GHz RSSI is lower than this threshold **/
+ public static int G_BAND_PREFERENCE_RSSI_THRESHOLD = -75;
+
+ /** @hide
+ * Boost given to RSSI on a home network for the purpose of calculating the score
+ * This adds stickiness to home networks, as defined by:
+ * - less than 4 known BSSIDs
+ * - PSK only
+ * - TODO: add a test to verify that all BSSIDs are behind same gateway
+ ***/
+ public static int HOME_NETWORK_RSSI_BOOST = 5;
+
/**
* @hide
* A summary of the RSSI and Band status for that configuration
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 54a7df2..e46f916 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -134,6 +134,11 @@
/**
* @hide
*/
+ public int linkStuckCount;
+
+ /**
+ * @hide
+ */
public int lowRssiCount;
/**
@@ -237,6 +242,7 @@
txRetriesRate = 0;
lowRssiCount = 0;
badRssiCount = 0;
+ linkStuckCount = 0;
score = 0;
}
@@ -267,6 +273,7 @@
score = source.score;
badRssiCount = source.badRssiCount;
lowRssiCount = source.lowRssiCount;
+ linkStuckCount = source.linkStuckCount;
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 141a69e..a30fb79 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2226,7 +2226,6 @@
}
}
-
/**
* Set wifi verbose log. Called from developer settings.
* @hide
@@ -2251,4 +2250,53 @@
return 0;
}
}
+
+ /**
+ * Set wifi Aggressive Handover. Called from developer settings.
+ * @hide
+ */
+ public void enableAggressiveHandover(int enabled) {
+ try {
+ mService.enableAggressiveHandover(enabled);
+ } catch (RemoteException e) {
+
+ }
+ }
+
+ /**
+ * Get the WiFi Handover aggressiveness.This is used by settings
+ * to decide what to show within the picker.
+ * @hide
+ */
+ public int getAggressiveHandover() {
+ try {
+ return mService.getAggressiveHandover();
+ } catch (RemoteException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Set setting for allowing Scans when traffic is ongoing.
+ * @hide
+ */
+ public void setAllowScansWithTraffic(int enabled) {
+ try {
+ mService.setAllowScansWithTraffic(enabled);
+ } catch (RemoteException e) {
+
+ }
+ }
+
+ /**
+ * Get setting for allowing Scans when traffic is ongoing.
+ * @hide
+ */
+ public int getAllowScansWithTraffic() {
+ try {
+ return mService.getAllowScansWithTraffic();
+ } catch (RemoteException e) {
+ return 0;
+ }
+ }
}